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