xref: /llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp (revision 0865ecc5150b9a55ba1f9e30b6d463a66ac362a6)
19c4b2225SAlexander Belyaev //===--- TextDiagnostics.cpp - Text Diagnostics for Paths -------*- C++ -*-===//
29c4b2225SAlexander Belyaev //
39c4b2225SAlexander Belyaev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
49c4b2225SAlexander Belyaev // See https://llvm.org/LICENSE.txt for license information.
59c4b2225SAlexander Belyaev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
69c4b2225SAlexander Belyaev //
79c4b2225SAlexander Belyaev //===----------------------------------------------------------------------===//
89c4b2225SAlexander Belyaev //
99c4b2225SAlexander Belyaev //  This file defines the TextDiagnostics object.
109c4b2225SAlexander Belyaev //
119c4b2225SAlexander Belyaev //===----------------------------------------------------------------------===//
129c4b2225SAlexander Belyaev 
137c58fb6bSBalazs Benics #include "clang/Analysis/MacroExpansionContext.h"
149c4b2225SAlexander Belyaev #include "clang/Analysis/PathDiagnostic.h"
159c4b2225SAlexander Belyaev #include "clang/Basic/SourceManager.h"
169c4b2225SAlexander Belyaev #include "clang/Basic/Version.h"
179c4b2225SAlexander Belyaev #include "clang/CrossTU/CrossTranslationUnit.h"
189c4b2225SAlexander Belyaev #include "clang/Frontend/ASTUnit.h"
199c4b2225SAlexander Belyaev #include "clang/Lex/Preprocessor.h"
209c4b2225SAlexander Belyaev #include "clang/Rewrite/Core/Rewriter.h"
219c4b2225SAlexander Belyaev #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
229c4b2225SAlexander Belyaev #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
239c4b2225SAlexander Belyaev #include "clang/Tooling/Core/Replacement.h"
249c4b2225SAlexander Belyaev #include "clang/Tooling/Tooling.h"
259c4b2225SAlexander Belyaev #include "llvm/ADT/SmallPtrSet.h"
269c4b2225SAlexander Belyaev #include "llvm/ADT/SmallVector.h"
279c4b2225SAlexander Belyaev #include "llvm/Support/Casting.h"
289c4b2225SAlexander Belyaev 
299c4b2225SAlexander Belyaev using namespace clang;
309c4b2225SAlexander Belyaev using namespace ento;
319c4b2225SAlexander Belyaev using namespace tooling;
329c4b2225SAlexander Belyaev 
339c4b2225SAlexander Belyaev namespace {
34a9f9f3dfSSprite /// Emits minimal diagnostics (report message + notes) for the 'none' output
35a9f9f3dfSSprite /// type to the standard error, or to complement many others. Emits detailed
369c4b2225SAlexander Belyaev /// diagnostics in textual format for the 'text' output type.
379c4b2225SAlexander Belyaev class TextDiagnostics : public PathDiagnosticConsumer {
389c4b2225SAlexander Belyaev   PathDiagnosticConsumerOptions DiagOpts;
399c4b2225SAlexander Belyaev   DiagnosticsEngine &DiagEng;
409c4b2225SAlexander Belyaev   const LangOptions &LO;
419c4b2225SAlexander Belyaev   bool ShouldDisplayPathNotes;
429c4b2225SAlexander Belyaev 
439c4b2225SAlexander Belyaev public:
449c4b2225SAlexander Belyaev   TextDiagnostics(PathDiagnosticConsumerOptions DiagOpts,
459c4b2225SAlexander Belyaev                   DiagnosticsEngine &DiagEng, const LangOptions &LO,
469c4b2225SAlexander Belyaev                   bool ShouldDisplayPathNotes)
479c4b2225SAlexander Belyaev       : DiagOpts(std::move(DiagOpts)), DiagEng(DiagEng), LO(LO),
489c4b2225SAlexander Belyaev         ShouldDisplayPathNotes(ShouldDisplayPathNotes) {}
499c4b2225SAlexander Belyaev   ~TextDiagnostics() override {}
509c4b2225SAlexander Belyaev 
519c4b2225SAlexander Belyaev   StringRef getName() const override { return "TextDiagnostics"; }
529c4b2225SAlexander Belyaev 
539c4b2225SAlexander Belyaev   bool supportsLogicalOpControlFlow() const override { return true; }
549c4b2225SAlexander Belyaev   bool supportsCrossFileDiagnostics() const override { return true; }
559c4b2225SAlexander Belyaev 
569c4b2225SAlexander Belyaev   PathGenerationScheme getGenerationScheme() const override {
579c4b2225SAlexander Belyaev     return ShouldDisplayPathNotes ? Minimal : None;
589c4b2225SAlexander Belyaev   }
599c4b2225SAlexander Belyaev 
609c4b2225SAlexander Belyaev   void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
619c4b2225SAlexander Belyaev                             FilesMade *filesMade) override {
629c4b2225SAlexander Belyaev     unsigned WarnID =
639c4b2225SAlexander Belyaev         DiagOpts.ShouldDisplayWarningsAsErrors
649c4b2225SAlexander Belyaev             ? DiagEng.getCustomDiagID(DiagnosticsEngine::Error, "%0")
659c4b2225SAlexander Belyaev             : DiagEng.getCustomDiagID(DiagnosticsEngine::Warning, "%0");
669c4b2225SAlexander Belyaev     unsigned NoteID = DiagEng.getCustomDiagID(DiagnosticsEngine::Note, "%0");
679c4b2225SAlexander Belyaev     SourceManager &SM = DiagEng.getSourceManager();
689c4b2225SAlexander Belyaev 
699c4b2225SAlexander Belyaev     Replacements Repls;
709c4b2225SAlexander Belyaev     auto reportPiece = [&](unsigned ID, FullSourceLoc Loc, StringRef String,
719c4b2225SAlexander Belyaev                            ArrayRef<SourceRange> Ranges,
729c4b2225SAlexander Belyaev                            ArrayRef<FixItHint> Fixits) {
739c4b2225SAlexander Belyaev       if (!DiagOpts.ShouldApplyFixIts) {
749c4b2225SAlexander Belyaev         DiagEng.Report(Loc, ID) << String << Ranges << Fixits;
759c4b2225SAlexander Belyaev         return;
769c4b2225SAlexander Belyaev       }
779c4b2225SAlexander Belyaev 
789c4b2225SAlexander Belyaev       DiagEng.Report(Loc, ID) << String << Ranges;
799c4b2225SAlexander Belyaev       for (const FixItHint &Hint : Fixits) {
809c4b2225SAlexander Belyaev         Replacement Repl(SM, Hint.RemoveRange, Hint.CodeToInsert);
819c4b2225SAlexander Belyaev 
829c4b2225SAlexander Belyaev         if (llvm::Error Err = Repls.add(Repl)) {
839c4b2225SAlexander Belyaev           llvm::errs() << "Error applying replacement " << Repl.toString()
84*9e333872SCongcong Cai                        << ": " << llvm::toString(std::move(Err)) << "\n";
859c4b2225SAlexander Belyaev         }
869c4b2225SAlexander Belyaev       }
879c4b2225SAlexander Belyaev     };
889c4b2225SAlexander Belyaev 
895c23e27bSBalazs Benics     for (const PathDiagnostic *PD : Diags) {
909c4b2225SAlexander Belyaev       std::string WarningMsg = (DiagOpts.ShouldDisplayDiagnosticName
919c4b2225SAlexander Belyaev                                     ? " [" + PD->getCheckerName() + "]"
929c4b2225SAlexander Belyaev                                     : "")
939c4b2225SAlexander Belyaev                                    .str();
949c4b2225SAlexander Belyaev       reportPiece(WarnID, PD->getLocation().asLocation(),
959c4b2225SAlexander Belyaev                   (PD->getShortDescription() + WarningMsg).str(),
969c4b2225SAlexander Belyaev                   PD->path.back()->getRanges(), PD->path.back()->getFixits());
979c4b2225SAlexander Belyaev 
989c4b2225SAlexander Belyaev       // First, add extra notes, even if paths should not be included.
999c4b2225SAlexander Belyaev       for (const auto &Piece : PD->path) {
1009c4b2225SAlexander Belyaev         if (!isa<PathDiagnosticNotePiece>(Piece.get()))
1019c4b2225SAlexander Belyaev           continue;
1029c4b2225SAlexander Belyaev 
1039c4b2225SAlexander Belyaev         reportPiece(NoteID, Piece->getLocation().asLocation(),
1049c4b2225SAlexander Belyaev                     Piece->getString(), Piece->getRanges(),
1059c4b2225SAlexander Belyaev                     Piece->getFixits());
1069c4b2225SAlexander Belyaev       }
1079c4b2225SAlexander Belyaev 
1089c4b2225SAlexander Belyaev       if (!ShouldDisplayPathNotes)
1099c4b2225SAlexander Belyaev         continue;
1109c4b2225SAlexander Belyaev 
1119c4b2225SAlexander Belyaev       // Then, add the path notes if necessary.
1129c4b2225SAlexander Belyaev       PathPieces FlatPath = PD->path.flatten(/*ShouldFlattenMacros=*/true);
1139c4b2225SAlexander Belyaev       for (const auto &Piece : FlatPath) {
1149c4b2225SAlexander Belyaev         if (isa<PathDiagnosticNotePiece>(Piece.get()))
1159c4b2225SAlexander Belyaev           continue;
1169c4b2225SAlexander Belyaev 
1179c4b2225SAlexander Belyaev         reportPiece(NoteID, Piece->getLocation().asLocation(),
1189c4b2225SAlexander Belyaev                     Piece->getString(), Piece->getRanges(),
1199c4b2225SAlexander Belyaev                     Piece->getFixits());
1209c4b2225SAlexander Belyaev       }
1219c4b2225SAlexander Belyaev     }
1229c4b2225SAlexander Belyaev 
1239c4b2225SAlexander Belyaev     if (Repls.empty())
1249c4b2225SAlexander Belyaev       return;
1259c4b2225SAlexander Belyaev 
1269c4b2225SAlexander Belyaev     Rewriter Rewrite(SM, LO);
1279c4b2225SAlexander Belyaev     if (!applyAllReplacements(Repls, Rewrite)) {
128f2c2e924SSylvestre Ledru       llvm::errs() << "An error occurred during applying fix-it.\n";
1299c4b2225SAlexander Belyaev     }
1309c4b2225SAlexander Belyaev 
1319c4b2225SAlexander Belyaev     Rewrite.overwriteChangedFiles();
1329c4b2225SAlexander Belyaev   }
1339c4b2225SAlexander Belyaev };
1349c4b2225SAlexander Belyaev } // end anonymous namespace
1359c4b2225SAlexander Belyaev 
1369c4b2225SAlexander Belyaev void ento::createTextPathDiagnosticConsumer(
1379c4b2225SAlexander Belyaev     PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
1387c58fb6bSBalazs Benics     const std::string &Prefix, const Preprocessor &PP,
1397c58fb6bSBalazs Benics     const cross_tu::CrossTranslationUnitContext &CTU,
1407c58fb6bSBalazs Benics     const MacroExpansionContext &MacroExpansions) {
1419c4b2225SAlexander Belyaev   C.emplace_back(new TextDiagnostics(std::move(DiagOpts), PP.getDiagnostics(),
1429c4b2225SAlexander Belyaev                                      PP.getLangOpts(),
1439c4b2225SAlexander Belyaev                                      /*ShouldDisplayPathNotes=*/true));
1449c4b2225SAlexander Belyaev }
1459c4b2225SAlexander Belyaev 
1469c4b2225SAlexander Belyaev void ento::createTextMinimalPathDiagnosticConsumer(
1479c4b2225SAlexander Belyaev     PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
1487c58fb6bSBalazs Benics     const std::string &Prefix, const Preprocessor &PP,
1497c58fb6bSBalazs Benics     const cross_tu::CrossTranslationUnitContext &CTU,
1507c58fb6bSBalazs Benics     const MacroExpansionContext &MacroExpansions) {
1519c4b2225SAlexander Belyaev   C.emplace_back(new TextDiagnostics(std::move(DiagOpts), PP.getDiagnostics(),
1529c4b2225SAlexander Belyaev                                      PP.getLangOpts(),
1539c4b2225SAlexander Belyaev                                      /*ShouldDisplayPathNotes=*/false));
1549c4b2225SAlexander Belyaev }
155