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 {
349c4b2225SAlexander Belyaev /// Emitsd minimal diagnostics (report message + notes) for the 'none' output
359c4b2225SAlexander Belyaev /// type to the standard error, or to to compliment 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:
TextDiagnostics(PathDiagnosticConsumerOptions DiagOpts,DiagnosticsEngine & DiagEng,const LangOptions & LO,bool ShouldDisplayPathNotes)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) {}
~TextDiagnostics()499c4b2225SAlexander Belyaev ~TextDiagnostics() override {}
509c4b2225SAlexander Belyaev
getName() const519c4b2225SAlexander Belyaev StringRef getName() const override { return "TextDiagnostics"; }
529c4b2225SAlexander Belyaev
supportsLogicalOpControlFlow() const539c4b2225SAlexander Belyaev bool supportsLogicalOpControlFlow() const override { return true; }
supportsCrossFileDiagnostics() const549c4b2225SAlexander Belyaev bool supportsCrossFileDiagnostics() const override { return true; }
559c4b2225SAlexander Belyaev
getGenerationScheme() const569c4b2225SAlexander Belyaev PathGenerationScheme getGenerationScheme() const override {
579c4b2225SAlexander Belyaev return ShouldDisplayPathNotes ? Minimal : None;
589c4b2225SAlexander Belyaev }
599c4b2225SAlexander Belyaev
FlushDiagnosticsImpl(std::vector<const PathDiagnostic * > & Diags,FilesMade * filesMade)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()
849c4b2225SAlexander Belyaev << ": " << Err << "\n";
859c4b2225SAlexander Belyaev }
869c4b2225SAlexander Belyaev }
879c4b2225SAlexander Belyaev };
889c4b2225SAlexander Belyaev
899c4b2225SAlexander Belyaev for (std::vector<const PathDiagnostic *>::iterator I = Diags.begin(),
909c4b2225SAlexander Belyaev E = Diags.end();
919c4b2225SAlexander Belyaev I != E; ++I) {
929c4b2225SAlexander Belyaev const PathDiagnostic *PD = *I;
939c4b2225SAlexander Belyaev std::string WarningMsg = (DiagOpts.ShouldDisplayDiagnosticName
949c4b2225SAlexander Belyaev ? " [" + PD->getCheckerName() + "]"
959c4b2225SAlexander Belyaev : "")
969c4b2225SAlexander Belyaev .str();
979c4b2225SAlexander Belyaev
989c4b2225SAlexander Belyaev reportPiece(WarnID, PD->getLocation().asLocation(),
999c4b2225SAlexander Belyaev (PD->getShortDescription() + WarningMsg).str(),
1009c4b2225SAlexander Belyaev PD->path.back()->getRanges(), PD->path.back()->getFixits());
1019c4b2225SAlexander Belyaev
1029c4b2225SAlexander Belyaev // First, add extra notes, even if paths should not be included.
1039c4b2225SAlexander Belyaev for (const auto &Piece : PD->path) {
1049c4b2225SAlexander Belyaev if (!isa<PathDiagnosticNotePiece>(Piece.get()))
1059c4b2225SAlexander Belyaev continue;
1069c4b2225SAlexander Belyaev
1079c4b2225SAlexander Belyaev reportPiece(NoteID, Piece->getLocation().asLocation(),
1089c4b2225SAlexander Belyaev Piece->getString(), Piece->getRanges(),
1099c4b2225SAlexander Belyaev Piece->getFixits());
1109c4b2225SAlexander Belyaev }
1119c4b2225SAlexander Belyaev
1129c4b2225SAlexander Belyaev if (!ShouldDisplayPathNotes)
1139c4b2225SAlexander Belyaev continue;
1149c4b2225SAlexander Belyaev
1159c4b2225SAlexander Belyaev // Then, add the path notes if necessary.
1169c4b2225SAlexander Belyaev PathPieces FlatPath = PD->path.flatten(/*ShouldFlattenMacros=*/true);
1179c4b2225SAlexander Belyaev for (const auto &Piece : FlatPath) {
1189c4b2225SAlexander Belyaev if (isa<PathDiagnosticNotePiece>(Piece.get()))
1199c4b2225SAlexander Belyaev continue;
1209c4b2225SAlexander Belyaev
1219c4b2225SAlexander Belyaev reportPiece(NoteID, Piece->getLocation().asLocation(),
1229c4b2225SAlexander Belyaev Piece->getString(), Piece->getRanges(),
1239c4b2225SAlexander Belyaev Piece->getFixits());
1249c4b2225SAlexander Belyaev }
1259c4b2225SAlexander Belyaev }
1269c4b2225SAlexander Belyaev
1279c4b2225SAlexander Belyaev if (Repls.empty())
1289c4b2225SAlexander Belyaev return;
1299c4b2225SAlexander Belyaev
1309c4b2225SAlexander Belyaev Rewriter Rewrite(SM, LO);
1319c4b2225SAlexander Belyaev if (!applyAllReplacements(Repls, Rewrite)) {
132*f2c2e924SSylvestre Ledru llvm::errs() << "An error occurred during applying fix-it.\n";
1339c4b2225SAlexander Belyaev }
1349c4b2225SAlexander Belyaev
1359c4b2225SAlexander Belyaev Rewrite.overwriteChangedFiles();
1369c4b2225SAlexander Belyaev }
1379c4b2225SAlexander Belyaev };
1389c4b2225SAlexander Belyaev } // end anonymous namespace
1399c4b2225SAlexander Belyaev
createTextPathDiagnosticConsumer(PathDiagnosticConsumerOptions DiagOpts,PathDiagnosticConsumers & C,const std::string & Prefix,const Preprocessor & PP,const cross_tu::CrossTranslationUnitContext & CTU,const MacroExpansionContext & MacroExpansions)1409c4b2225SAlexander Belyaev void ento::createTextPathDiagnosticConsumer(
1419c4b2225SAlexander Belyaev PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
1427c58fb6bSBalazs Benics const std::string &Prefix, const Preprocessor &PP,
1437c58fb6bSBalazs Benics const cross_tu::CrossTranslationUnitContext &CTU,
1447c58fb6bSBalazs Benics const MacroExpansionContext &MacroExpansions) {
1459c4b2225SAlexander Belyaev C.emplace_back(new TextDiagnostics(std::move(DiagOpts), PP.getDiagnostics(),
1469c4b2225SAlexander Belyaev PP.getLangOpts(),
1479c4b2225SAlexander Belyaev /*ShouldDisplayPathNotes=*/true));
1489c4b2225SAlexander Belyaev }
1499c4b2225SAlexander Belyaev
createTextMinimalPathDiagnosticConsumer(PathDiagnosticConsumerOptions DiagOpts,PathDiagnosticConsumers & C,const std::string & Prefix,const Preprocessor & PP,const cross_tu::CrossTranslationUnitContext & CTU,const MacroExpansionContext & MacroExpansions)1509c4b2225SAlexander Belyaev void ento::createTextMinimalPathDiagnosticConsumer(
1519c4b2225SAlexander Belyaev PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
1527c58fb6bSBalazs Benics const std::string &Prefix, const Preprocessor &PP,
1537c58fb6bSBalazs Benics const cross_tu::CrossTranslationUnitContext &CTU,
1547c58fb6bSBalazs Benics const MacroExpansionContext &MacroExpansions) {
1559c4b2225SAlexander Belyaev C.emplace_back(new TextDiagnostics(std::move(DiagOpts), PP.getDiagnostics(),
1569c4b2225SAlexander Belyaev PP.getLangOpts(),
1579c4b2225SAlexander Belyaev /*ShouldDisplayPathNotes=*/false));
1589c4b2225SAlexander Belyaev }
159