1 //===--- TextDiagnostics.cpp - Text Diagnostics for Paths -------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines the TextDiagnostics object. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "clang/Analysis/PathDiagnostic.h" 14 #include "clang/Basic/SourceManager.h" 15 #include "clang/Basic/Version.h" 16 #include "clang/CrossTU/CrossTranslationUnit.h" 17 #include "clang/Frontend/ASTUnit.h" 18 #include "clang/Lex/Preprocessor.h" 19 #include "clang/Rewrite/Core/Rewriter.h" 20 #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" 21 #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" 22 #include "clang/Tooling/Core/Replacement.h" 23 #include "clang/Tooling/Tooling.h" 24 #include "llvm/ADT/SmallPtrSet.h" 25 #include "llvm/ADT/SmallVector.h" 26 #include "llvm/Support/Casting.h" 27 28 using namespace clang; 29 using namespace ento; 30 using namespace tooling; 31 32 namespace { 33 /// Emitsd minimal diagnostics (report message + notes) for the 'none' output 34 /// type to the standard error, or to to compliment many others. Emits detailed 35 /// diagnostics in textual format for the 'text' output type. 36 class TextDiagnostics : public PathDiagnosticConsumer { 37 DiagnosticsEngine &DiagEng; 38 const LangOptions &LO; 39 const bool IncludePath = false; 40 const bool ShouldEmitAsError = false; 41 const bool ApplyFixIts = false; 42 const bool ShouldDisplayCheckerName = false; 43 44 public: 45 TextDiagnostics(DiagnosticsEngine &DiagEng, const LangOptions &LO, 46 bool ShouldIncludePath, const AnalyzerOptions &AnOpts) 47 : DiagEng(DiagEng), LO(LO), IncludePath(ShouldIncludePath), 48 ShouldEmitAsError(AnOpts.AnalyzerWerror), 49 ApplyFixIts(AnOpts.ShouldApplyFixIts), 50 ShouldDisplayCheckerName(AnOpts.ShouldDisplayCheckerNameForText) {} 51 ~TextDiagnostics() override {} 52 53 StringRef getName() const override { return "TextDiagnostics"; } 54 55 bool supportsLogicalOpControlFlow() const override { return true; } 56 bool supportsCrossFileDiagnostics() const override { return true; } 57 58 PathGenerationScheme getGenerationScheme() const override { 59 return IncludePath ? Minimal : None; 60 } 61 62 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, 63 FilesMade *filesMade) override { 64 unsigned WarnID = 65 ShouldEmitAsError 66 ? DiagEng.getCustomDiagID(DiagnosticsEngine::Error, "%0") 67 : DiagEng.getCustomDiagID(DiagnosticsEngine::Warning, "%0"); 68 unsigned NoteID = DiagEng.getCustomDiagID(DiagnosticsEngine::Note, "%0"); 69 SourceManager &SM = DiagEng.getSourceManager(); 70 71 Replacements Repls; 72 auto reportPiece = [&](unsigned ID, FullSourceLoc Loc, StringRef String, 73 ArrayRef<SourceRange> Ranges, 74 ArrayRef<FixItHint> Fixits) { 75 if (!ApplyFixIts) { 76 DiagEng.Report(Loc, ID) << String << Ranges << Fixits; 77 return; 78 } 79 80 DiagEng.Report(Loc, ID) << String << Ranges; 81 for (const FixItHint &Hint : Fixits) { 82 Replacement Repl(SM, Hint.RemoveRange, Hint.CodeToInsert); 83 84 if (llvm::Error Err = Repls.add(Repl)) { 85 llvm::errs() << "Error applying replacement " << Repl.toString() 86 << ": " << Err << "\n"; 87 } 88 } 89 }; 90 91 for (std::vector<const PathDiagnostic *>::iterator I = Diags.begin(), 92 E = Diags.end(); 93 I != E; ++I) { 94 const PathDiagnostic *PD = *I; 95 std::string WarningMsg = 96 (ShouldDisplayCheckerName ? " [" + PD->getCheckerName() + "]" : "") 97 .str(); 98 99 reportPiece(WarnID, PD->getLocation().asLocation(), 100 (PD->getShortDescription() + WarningMsg).str(), 101 PD->path.back()->getRanges(), PD->path.back()->getFixits()); 102 103 // First, add extra notes, even if paths should not be included. 104 for (const auto &Piece : PD->path) { 105 if (!isa<PathDiagnosticNotePiece>(Piece.get())) 106 continue; 107 108 reportPiece(NoteID, Piece->getLocation().asLocation(), 109 Piece->getString(), Piece->getRanges(), 110 Piece->getFixits()); 111 } 112 113 if (!IncludePath) 114 continue; 115 116 // Then, add the path notes if necessary. 117 PathPieces FlatPath = PD->path.flatten(/*ShouldFlattenMacros=*/true); 118 for (const auto &Piece : FlatPath) { 119 if (isa<PathDiagnosticNotePiece>(Piece.get())) 120 continue; 121 122 reportPiece(NoteID, Piece->getLocation().asLocation(), 123 Piece->getString(), Piece->getRanges(), 124 Piece->getFixits()); 125 } 126 } 127 128 if (!ApplyFixIts || Repls.empty()) 129 return; 130 131 Rewriter Rewrite(SM, LO); 132 if (!applyAllReplacements(Repls, Rewrite)) { 133 llvm::errs() << "An error occured during applying fix-it.\n"; 134 } 135 136 Rewrite.overwriteChangedFiles(); 137 } 138 }; 139 } // end anonymous namespace 140 141 void ento::createTextPathDiagnosticConsumer( 142 AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, 143 const std::string &Prefix, const clang::Preprocessor &PP, 144 const cross_tu::CrossTranslationUnitContext &CTU) { 145 C.emplace_back(new TextDiagnostics(PP.getDiagnostics(), PP.getLangOpts(), 146 /*ShouldIncludePath*/ true, AnalyzerOpts)); 147 } 148 149 void ento::createTextMinimalPathDiagnosticConsumer( 150 AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, 151 const std::string &Prefix, const clang::Preprocessor &PP, 152 const cross_tu::CrossTranslationUnitContext &CTU) { 153 C.emplace_back(new TextDiagnostics(PP.getDiagnostics(), PP.getLangOpts(), 154 /*ShouldIncludePath*/ false, 155 AnalyzerOpts)); 156 } 157