xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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