diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp')
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp new file mode 100644 index 0000000000000..f4c7e5978e193 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp @@ -0,0 +1,156 @@ +//===--- TextDiagnostics.cpp - Text Diagnostics for Paths -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the TextDiagnostics object. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathDiagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/Version.h" +#include "clang/CrossTU/CrossTranslationUnit.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" +#include "clang/Tooling/Core/Replacement.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" + +using namespace clang; +using namespace ento; +using namespace tooling; + +namespace { +/// Emitsd minimal diagnostics (report message + notes) for the 'none' output +/// type to the standard error, or to to compliment many others. Emits detailed +/// diagnostics in textual format for the 'text' output type. +class TextDiagnostics : public PathDiagnosticConsumer { + DiagnosticsEngine &DiagEng; + const LangOptions &LO; + const bool IncludePath = false; + const bool ShouldEmitAsError = false; + const bool ApplyFixIts = false; + const bool ShouldDisplayCheckerName = false; + +public: + TextDiagnostics(DiagnosticsEngine &DiagEng, const LangOptions &LO, + bool ShouldIncludePath, const AnalyzerOptions &AnOpts) + : DiagEng(DiagEng), LO(LO), IncludePath(ShouldIncludePath), + ShouldEmitAsError(AnOpts.AnalyzerWerror), + ApplyFixIts(AnOpts.ShouldApplyFixIts), + ShouldDisplayCheckerName(AnOpts.ShouldDisplayCheckerNameForText) {} + ~TextDiagnostics() override {} + + StringRef getName() const override { return "TextDiagnostics"; } + + bool supportsLogicalOpControlFlow() const override { return true; } + bool supportsCrossFileDiagnostics() const override { return true; } + + PathGenerationScheme getGenerationScheme() const override { + return IncludePath ? Minimal : None; + } + + void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, + FilesMade *filesMade) override { + unsigned WarnID = + ShouldEmitAsError + ? DiagEng.getCustomDiagID(DiagnosticsEngine::Error, "%0") + : DiagEng.getCustomDiagID(DiagnosticsEngine::Warning, "%0"); + unsigned NoteID = DiagEng.getCustomDiagID(DiagnosticsEngine::Note, "%0"); + SourceManager &SM = DiagEng.getSourceManager(); + + Replacements Repls; + auto reportPiece = [&](unsigned ID, FullSourceLoc Loc, StringRef String, + ArrayRef<SourceRange> Ranges, + ArrayRef<FixItHint> Fixits) { + if (!ApplyFixIts) { + DiagEng.Report(Loc, ID) << String << Ranges << Fixits; + return; + } + + DiagEng.Report(Loc, ID) << String << Ranges; + for (const FixItHint &Hint : Fixits) { + Replacement Repl(SM, Hint.RemoveRange, Hint.CodeToInsert); + + if (llvm::Error Err = Repls.add(Repl)) { + llvm::errs() << "Error applying replacement " << Repl.toString() + << ": " << Err << "\n"; + } + } + }; + + for (std::vector<const PathDiagnostic *>::iterator I = Diags.begin(), + E = Diags.end(); + I != E; ++I) { + const PathDiagnostic *PD = *I; + std::string WarningMsg = + (ShouldDisplayCheckerName ? " [" + PD->getCheckerName() + "]" : "") + .str(); + + reportPiece(WarnID, PD->getLocation().asLocation(), + (PD->getShortDescription() + WarningMsg).str(), + PD->path.back()->getRanges(), PD->path.back()->getFixits()); + + // First, add extra notes, even if paths should not be included. + for (const auto &Piece : PD->path) { + if (!isa<PathDiagnosticNotePiece>(Piece.get())) + continue; + + reportPiece(NoteID, Piece->getLocation().asLocation(), + Piece->getString(), Piece->getRanges(), + Piece->getFixits()); + } + + if (!IncludePath) + continue; + + // Then, add the path notes if necessary. + PathPieces FlatPath = PD->path.flatten(/*ShouldFlattenMacros=*/true); + for (const auto &Piece : FlatPath) { + if (isa<PathDiagnosticNotePiece>(Piece.get())) + continue; + + reportPiece(NoteID, Piece->getLocation().asLocation(), + Piece->getString(), Piece->getRanges(), + Piece->getFixits()); + } + } + + if (!ApplyFixIts || Repls.empty()) + return; + + Rewriter Rewrite(SM, LO); + if (!applyAllReplacements(Repls, Rewrite)) { + llvm::errs() << "An error occured during applying fix-it.\n"; + } + + Rewrite.overwriteChangedFiles(); + } +}; +} // end anonymous namespace + +void ento::createTextPathDiagnosticConsumer( + AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + const std::string &Prefix, const clang::Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU) { + C.emplace_back(new TextDiagnostics(PP.getDiagnostics(), PP.getLangOpts(), + /*ShouldIncludePath*/ true, AnalyzerOpts)); +} + +void ento::createTextMinimalPathDiagnosticConsumer( + AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + const std::string &Prefix, const clang::Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU) { + C.emplace_back(new TextDiagnostics(PP.getDiagnostics(), PP.getLangOpts(), + /*ShouldIncludePath*/ false, + AnalyzerOpts)); +} |