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/MacroExpansionContext.h"
14 #include "clang/Analysis/PathDiagnostic.h"
15 #include "clang/Basic/SourceManager.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/PathDiagnosticConsumers.h"
21 #include "clang/Tooling/Core/Replacement.h"
22 #include "clang/Tooling/Tooling.h"
23
24 using namespace clang;
25 using namespace ento;
26 using namespace tooling;
27
28 namespace {
29 /// Emits minimal diagnostics (report message + notes) for the 'none' output
30 /// type to the standard error, or to complement many others. Emits detailed
31 /// diagnostics in textual format for the 'text' output type.
32 class TextDiagnostics : public PathDiagnosticConsumer {
33 PathDiagnosticConsumerOptions DiagOpts;
34 DiagnosticsEngine &DiagEng;
35 const LangOptions &LO;
36 bool ShouldDisplayPathNotes;
37
38 public:
TextDiagnostics(PathDiagnosticConsumerOptions DiagOpts,DiagnosticsEngine & DiagEng,const LangOptions & LO,bool ShouldDisplayPathNotes)39 TextDiagnostics(PathDiagnosticConsumerOptions DiagOpts,
40 DiagnosticsEngine &DiagEng, const LangOptions &LO,
41 bool ShouldDisplayPathNotes)
42 : DiagOpts(std::move(DiagOpts)), DiagEng(DiagEng), LO(LO),
43 ShouldDisplayPathNotes(ShouldDisplayPathNotes) {}
~TextDiagnostics()44 ~TextDiagnostics() override {}
45
getName() const46 StringRef getName() const override { return "TextDiagnostics"; }
47
supportsLogicalOpControlFlow() const48 bool supportsLogicalOpControlFlow() const override { return true; }
supportsCrossFileDiagnostics() const49 bool supportsCrossFileDiagnostics() const override { return true; }
50
getGenerationScheme() const51 PathGenerationScheme getGenerationScheme() const override {
52 return ShouldDisplayPathNotes ? Minimal : None;
53 }
54
FlushDiagnosticsImpl(std::vector<const PathDiagnostic * > & Diags,FilesMade * filesMade)55 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
56 FilesMade *filesMade) override {
57 unsigned WarnID =
58 DiagOpts.ShouldDisplayWarningsAsErrors
59 ? DiagEng.getCustomDiagID(DiagnosticsEngine::Error, "%0")
60 : DiagEng.getCustomDiagID(DiagnosticsEngine::Warning, "%0");
61 unsigned NoteID = DiagEng.getCustomDiagID(DiagnosticsEngine::Note, "%0");
62 SourceManager &SM = DiagEng.getSourceManager();
63
64 Replacements Repls;
65 auto reportPiece = [&](unsigned ID, FullSourceLoc Loc, StringRef String,
66 ArrayRef<SourceRange> Ranges,
67 ArrayRef<FixItHint> Fixits) {
68 if (!DiagOpts.ShouldApplyFixIts) {
69 DiagEng.Report(Loc, ID) << String << Ranges << Fixits;
70 return;
71 }
72
73 DiagEng.Report(Loc, ID) << String << Ranges;
74 for (const FixItHint &Hint : Fixits) {
75 Replacement Repl(SM, Hint.RemoveRange, Hint.CodeToInsert);
76
77 if (llvm::Error Err = Repls.add(Repl)) {
78 llvm::errs() << "Error applying replacement " << Repl.toString()
79 << ": " << llvm::toString(std::move(Err)) << "\n";
80 }
81 }
82 };
83
84 for (const PathDiagnostic *PD : Diags) {
85 std::string WarningMsg = (DiagOpts.ShouldDisplayDiagnosticName
86 ? " [" + PD->getCheckerName() + "]"
87 : "")
88 .str();
89 reportPiece(WarnID, PD->getLocation().asLocation(),
90 (PD->getShortDescription() + WarningMsg).str(),
91 PD->path.back()->getRanges(), PD->path.back()->getFixits());
92
93 // First, add extra notes, even if paths should not be included.
94 for (const auto &Piece : PD->path) {
95 if (!isa<PathDiagnosticNotePiece>(Piece.get()))
96 continue;
97
98 reportPiece(NoteID, Piece->getLocation().asLocation(),
99 Piece->getString(), Piece->getRanges(),
100 Piece->getFixits());
101 }
102
103 if (!ShouldDisplayPathNotes)
104 continue;
105
106 // Then, add the path notes if necessary.
107 PathPieces FlatPath = PD->path.flatten(/*ShouldFlattenMacros=*/true);
108 for (const auto &Piece : FlatPath) {
109 if (isa<PathDiagnosticNotePiece>(Piece.get()))
110 continue;
111
112 reportPiece(NoteID, Piece->getLocation().asLocation(),
113 Piece->getString(), Piece->getRanges(),
114 Piece->getFixits());
115 }
116 }
117
118 if (Repls.empty())
119 return;
120
121 Rewriter Rewrite(SM, LO);
122 if (!applyAllReplacements(Repls, Rewrite)) {
123 llvm::errs() << "An error occurred during applying fix-it.\n";
124 }
125
126 Rewrite.overwriteChangedFiles();
127 }
128 };
129 } // end anonymous namespace
130
createTextPathDiagnosticConsumer(PathDiagnosticConsumerOptions DiagOpts,PathDiagnosticConsumers & C,const std::string & Prefix,const Preprocessor & PP,const cross_tu::CrossTranslationUnitContext & CTU,const MacroExpansionContext & MacroExpansions)131 void ento::createTextPathDiagnosticConsumer(
132 PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
133 const std::string &Prefix, const Preprocessor &PP,
134 const cross_tu::CrossTranslationUnitContext &CTU,
135 const MacroExpansionContext &MacroExpansions) {
136 C.emplace_back(new TextDiagnostics(std::move(DiagOpts), PP.getDiagnostics(),
137 PP.getLangOpts(),
138 /*ShouldDisplayPathNotes=*/true));
139 }
140
createTextMinimalPathDiagnosticConsumer(PathDiagnosticConsumerOptions DiagOpts,PathDiagnosticConsumers & C,const std::string & Prefix,const Preprocessor & PP,const cross_tu::CrossTranslationUnitContext & CTU,const MacroExpansionContext & MacroExpansions)141 void ento::createTextMinimalPathDiagnosticConsumer(
142 PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
143 const std::string &Prefix, const Preprocessor &PP,
144 const cross_tu::CrossTranslationUnitContext &CTU,
145 const MacroExpansionContext &MacroExpansions) {
146 C.emplace_back(new TextDiagnostics(std::move(DiagOpts), PP.getDiagnostics(),
147 PP.getLangOpts(),
148 /*ShouldDisplayPathNotes=*/false));
149 }
150