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 PathDiagnosticConsumerOptions DiagOpts; 38 DiagnosticsEngine &DiagEng; 39 const LangOptions &LO; 40 bool ShouldDisplayPathNotes; 41 42 public: 43 TextDiagnostics(PathDiagnosticConsumerOptions DiagOpts, 44 DiagnosticsEngine &DiagEng, const LangOptions &LO, 45 bool ShouldDisplayPathNotes) 46 : DiagOpts(std::move(DiagOpts)), DiagEng(DiagEng), LO(LO), 47 ShouldDisplayPathNotes(ShouldDisplayPathNotes) {} 48 ~TextDiagnostics() override {} 49 50 StringRef getName() const override { return "TextDiagnostics"; } 51 52 bool supportsLogicalOpControlFlow() const override { return true; } 53 bool supportsCrossFileDiagnostics() const override { return true; } 54 55 PathGenerationScheme getGenerationScheme() const override { 56 return ShouldDisplayPathNotes ? Minimal : None; 57 } 58 59 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, 60 FilesMade *filesMade) override { 61 unsigned WarnID = 62 DiagOpts.ShouldDisplayWarningsAsErrors 63 ? DiagEng.getCustomDiagID(DiagnosticsEngine::Error, "%0") 64 : DiagEng.getCustomDiagID(DiagnosticsEngine::Warning, "%0"); 65 unsigned NoteID = DiagEng.getCustomDiagID(DiagnosticsEngine::Note, "%0"); 66 SourceManager &SM = DiagEng.getSourceManager(); 67 68 Replacements Repls; 69 auto reportPiece = [&](unsigned ID, FullSourceLoc Loc, StringRef String, 70 ArrayRef<SourceRange> Ranges, 71 ArrayRef<FixItHint> Fixits) { 72 if (!DiagOpts.ShouldApplyFixIts) { 73 DiagEng.Report(Loc, ID) << String << Ranges << Fixits; 74 return; 75 } 76 77 DiagEng.Report(Loc, ID) << String << Ranges; 78 for (const FixItHint &Hint : Fixits) { 79 Replacement Repl(SM, Hint.RemoveRange, Hint.CodeToInsert); 80 81 if (llvm::Error Err = Repls.add(Repl)) { 82 llvm::errs() << "Error applying replacement " << Repl.toString() 83 << ": " << Err << "\n"; 84 } 85 } 86 }; 87 88 for (std::vector<const PathDiagnostic *>::iterator I = Diags.begin(), 89 E = Diags.end(); 90 I != E; ++I) { 91 const PathDiagnostic *PD = *I; 92 std::string WarningMsg = (DiagOpts.ShouldDisplayDiagnosticName 93 ? " [" + PD->getCheckerName() + "]" 94 : "") 95 .str(); 96 97 reportPiece(WarnID, PD->getLocation().asLocation(), 98 (PD->getShortDescription() + WarningMsg).str(), 99 PD->path.back()->getRanges(), PD->path.back()->getFixits()); 100 101 // First, add extra notes, even if paths should not be included. 102 for (const auto &Piece : PD->path) { 103 if (!isa<PathDiagnosticNotePiece>(Piece.get())) 104 continue; 105 106 reportPiece(NoteID, Piece->getLocation().asLocation(), 107 Piece->getString(), Piece->getRanges(), 108 Piece->getFixits()); 109 } 110 111 if (!ShouldDisplayPathNotes) 112 continue; 113 114 // Then, add the path notes if necessary. 115 PathPieces FlatPath = PD->path.flatten(/*ShouldFlattenMacros=*/true); 116 for (const auto &Piece : FlatPath) { 117 if (isa<PathDiagnosticNotePiece>(Piece.get())) 118 continue; 119 120 reportPiece(NoteID, Piece->getLocation().asLocation(), 121 Piece->getString(), Piece->getRanges(), 122 Piece->getFixits()); 123 } 124 } 125 126 if (Repls.empty()) 127 return; 128 129 Rewriter Rewrite(SM, LO); 130 if (!applyAllReplacements(Repls, Rewrite)) { 131 llvm::errs() << "An error occured during applying fix-it.\n"; 132 } 133 134 Rewrite.overwriteChangedFiles(); 135 } 136 }; 137 } // end anonymous namespace 138 139 void ento::createTextPathDiagnosticConsumer( 140 PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C, 141 const std::string &Prefix, const clang::Preprocessor &PP, 142 const cross_tu::CrossTranslationUnitContext &CTU) { 143 C.emplace_back(new TextDiagnostics(std::move(DiagOpts), PP.getDiagnostics(), 144 PP.getLangOpts(), 145 /*ShouldDisplayPathNotes=*/true)); 146 } 147 148 void ento::createTextMinimalPathDiagnosticConsumer( 149 PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C, 150 const std::string &Prefix, const clang::Preprocessor &PP, 151 const cross_tu::CrossTranslationUnitContext &CTU) { 152 C.emplace_back(new TextDiagnostics(std::move(DiagOpts), PP.getDiagnostics(), 153 PP.getLangOpts(), 154 /*ShouldDisplayPathNotes=*/false)); 155 } 156