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