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