15ffd83dbSDimitry Andric //===--- TextDiagnostics.cpp - Text Diagnostics for Paths -------*- C++ -*-===// 25ffd83dbSDimitry Andric // 35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65ffd83dbSDimitry Andric // 75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 85ffd83dbSDimitry Andric // 95ffd83dbSDimitry Andric // This file defines the TextDiagnostics object. 105ffd83dbSDimitry Andric // 115ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 125ffd83dbSDimitry Andric 135ffd83dbSDimitry Andric #include "clang/Analysis/PathDiagnostic.h" 145ffd83dbSDimitry Andric #include "clang/Basic/SourceManager.h" 155ffd83dbSDimitry Andric #include "clang/Basic/Version.h" 165ffd83dbSDimitry Andric #include "clang/CrossTU/CrossTranslationUnit.h" 175ffd83dbSDimitry Andric #include "clang/Frontend/ASTUnit.h" 185ffd83dbSDimitry Andric #include "clang/Lex/Preprocessor.h" 195ffd83dbSDimitry Andric #include "clang/Rewrite/Core/Rewriter.h" 205ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" 215ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" 225ffd83dbSDimitry Andric #include "clang/Tooling/Core/Replacement.h" 235ffd83dbSDimitry Andric #include "clang/Tooling/Tooling.h" 245ffd83dbSDimitry Andric #include "llvm/ADT/SmallPtrSet.h" 255ffd83dbSDimitry Andric #include "llvm/ADT/SmallVector.h" 265ffd83dbSDimitry Andric #include "llvm/Support/Casting.h" 275ffd83dbSDimitry Andric 285ffd83dbSDimitry Andric using namespace clang; 295ffd83dbSDimitry Andric using namespace ento; 305ffd83dbSDimitry Andric using namespace tooling; 315ffd83dbSDimitry Andric 325ffd83dbSDimitry Andric namespace { 335ffd83dbSDimitry Andric /// Emitsd minimal diagnostics (report message + notes) for the 'none' output 345ffd83dbSDimitry Andric /// type to the standard error, or to to compliment many others. Emits detailed 355ffd83dbSDimitry Andric /// diagnostics in textual format for the 'text' output type. 365ffd83dbSDimitry Andric class TextDiagnostics : public PathDiagnosticConsumer { 37*e8d8bef9SDimitry Andric PathDiagnosticConsumerOptions DiagOpts; 385ffd83dbSDimitry Andric DiagnosticsEngine &DiagEng; 395ffd83dbSDimitry Andric const LangOptions &LO; 40*e8d8bef9SDimitry Andric bool ShouldDisplayPathNotes; 415ffd83dbSDimitry Andric 425ffd83dbSDimitry Andric public: 43*e8d8bef9SDimitry Andric TextDiagnostics(PathDiagnosticConsumerOptions DiagOpts, 44*e8d8bef9SDimitry Andric DiagnosticsEngine &DiagEng, const LangOptions &LO, 45*e8d8bef9SDimitry Andric bool ShouldDisplayPathNotes) 46*e8d8bef9SDimitry Andric : DiagOpts(std::move(DiagOpts)), DiagEng(DiagEng), LO(LO), 47*e8d8bef9SDimitry Andric ShouldDisplayPathNotes(ShouldDisplayPathNotes) {} 485ffd83dbSDimitry Andric ~TextDiagnostics() override {} 495ffd83dbSDimitry Andric 505ffd83dbSDimitry Andric StringRef getName() const override { return "TextDiagnostics"; } 515ffd83dbSDimitry Andric 525ffd83dbSDimitry Andric bool supportsLogicalOpControlFlow() const override { return true; } 535ffd83dbSDimitry Andric bool supportsCrossFileDiagnostics() const override { return true; } 545ffd83dbSDimitry Andric 555ffd83dbSDimitry Andric PathGenerationScheme getGenerationScheme() const override { 56*e8d8bef9SDimitry Andric return ShouldDisplayPathNotes ? Minimal : None; 575ffd83dbSDimitry Andric } 585ffd83dbSDimitry Andric 595ffd83dbSDimitry Andric void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, 605ffd83dbSDimitry Andric FilesMade *filesMade) override { 615ffd83dbSDimitry Andric unsigned WarnID = 62*e8d8bef9SDimitry Andric DiagOpts.ShouldDisplayWarningsAsErrors 635ffd83dbSDimitry Andric ? DiagEng.getCustomDiagID(DiagnosticsEngine::Error, "%0") 645ffd83dbSDimitry Andric : DiagEng.getCustomDiagID(DiagnosticsEngine::Warning, "%0"); 655ffd83dbSDimitry Andric unsigned NoteID = DiagEng.getCustomDiagID(DiagnosticsEngine::Note, "%0"); 665ffd83dbSDimitry Andric SourceManager &SM = DiagEng.getSourceManager(); 675ffd83dbSDimitry Andric 685ffd83dbSDimitry Andric Replacements Repls; 695ffd83dbSDimitry Andric auto reportPiece = [&](unsigned ID, FullSourceLoc Loc, StringRef String, 705ffd83dbSDimitry Andric ArrayRef<SourceRange> Ranges, 715ffd83dbSDimitry Andric ArrayRef<FixItHint> Fixits) { 72*e8d8bef9SDimitry Andric if (!DiagOpts.ShouldApplyFixIts) { 735ffd83dbSDimitry Andric DiagEng.Report(Loc, ID) << String << Ranges << Fixits; 745ffd83dbSDimitry Andric return; 755ffd83dbSDimitry Andric } 765ffd83dbSDimitry Andric 775ffd83dbSDimitry Andric DiagEng.Report(Loc, ID) << String << Ranges; 785ffd83dbSDimitry Andric for (const FixItHint &Hint : Fixits) { 795ffd83dbSDimitry Andric Replacement Repl(SM, Hint.RemoveRange, Hint.CodeToInsert); 805ffd83dbSDimitry Andric 815ffd83dbSDimitry Andric if (llvm::Error Err = Repls.add(Repl)) { 825ffd83dbSDimitry Andric llvm::errs() << "Error applying replacement " << Repl.toString() 835ffd83dbSDimitry Andric << ": " << Err << "\n"; 845ffd83dbSDimitry Andric } 855ffd83dbSDimitry Andric } 865ffd83dbSDimitry Andric }; 875ffd83dbSDimitry Andric 885ffd83dbSDimitry Andric for (std::vector<const PathDiagnostic *>::iterator I = Diags.begin(), 895ffd83dbSDimitry Andric E = Diags.end(); 905ffd83dbSDimitry Andric I != E; ++I) { 915ffd83dbSDimitry Andric const PathDiagnostic *PD = *I; 92*e8d8bef9SDimitry Andric std::string WarningMsg = (DiagOpts.ShouldDisplayDiagnosticName 93*e8d8bef9SDimitry Andric ? " [" + PD->getCheckerName() + "]" 94*e8d8bef9SDimitry Andric : "") 955ffd83dbSDimitry Andric .str(); 965ffd83dbSDimitry Andric 975ffd83dbSDimitry Andric reportPiece(WarnID, PD->getLocation().asLocation(), 985ffd83dbSDimitry Andric (PD->getShortDescription() + WarningMsg).str(), 995ffd83dbSDimitry Andric PD->path.back()->getRanges(), PD->path.back()->getFixits()); 1005ffd83dbSDimitry Andric 1015ffd83dbSDimitry Andric // First, add extra notes, even if paths should not be included. 1025ffd83dbSDimitry Andric for (const auto &Piece : PD->path) { 1035ffd83dbSDimitry Andric if (!isa<PathDiagnosticNotePiece>(Piece.get())) 1045ffd83dbSDimitry Andric continue; 1055ffd83dbSDimitry Andric 1065ffd83dbSDimitry Andric reportPiece(NoteID, Piece->getLocation().asLocation(), 1075ffd83dbSDimitry Andric Piece->getString(), Piece->getRanges(), 1085ffd83dbSDimitry Andric Piece->getFixits()); 1095ffd83dbSDimitry Andric } 1105ffd83dbSDimitry Andric 111*e8d8bef9SDimitry Andric if (!ShouldDisplayPathNotes) 1125ffd83dbSDimitry Andric continue; 1135ffd83dbSDimitry Andric 1145ffd83dbSDimitry Andric // Then, add the path notes if necessary. 1155ffd83dbSDimitry Andric PathPieces FlatPath = PD->path.flatten(/*ShouldFlattenMacros=*/true); 1165ffd83dbSDimitry Andric for (const auto &Piece : FlatPath) { 1175ffd83dbSDimitry Andric if (isa<PathDiagnosticNotePiece>(Piece.get())) 1185ffd83dbSDimitry Andric continue; 1195ffd83dbSDimitry Andric 1205ffd83dbSDimitry Andric reportPiece(NoteID, Piece->getLocation().asLocation(), 1215ffd83dbSDimitry Andric Piece->getString(), Piece->getRanges(), 1225ffd83dbSDimitry Andric Piece->getFixits()); 1235ffd83dbSDimitry Andric } 1245ffd83dbSDimitry Andric } 1255ffd83dbSDimitry Andric 126*e8d8bef9SDimitry Andric if (Repls.empty()) 1275ffd83dbSDimitry Andric return; 1285ffd83dbSDimitry Andric 1295ffd83dbSDimitry Andric Rewriter Rewrite(SM, LO); 1305ffd83dbSDimitry Andric if (!applyAllReplacements(Repls, Rewrite)) { 1315ffd83dbSDimitry Andric llvm::errs() << "An error occured during applying fix-it.\n"; 1325ffd83dbSDimitry Andric } 1335ffd83dbSDimitry Andric 1345ffd83dbSDimitry Andric Rewrite.overwriteChangedFiles(); 1355ffd83dbSDimitry Andric } 1365ffd83dbSDimitry Andric }; 1375ffd83dbSDimitry Andric } // end anonymous namespace 1385ffd83dbSDimitry Andric 1395ffd83dbSDimitry Andric void ento::createTextPathDiagnosticConsumer( 140*e8d8bef9SDimitry Andric PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C, 1415ffd83dbSDimitry Andric const std::string &Prefix, const clang::Preprocessor &PP, 1425ffd83dbSDimitry Andric const cross_tu::CrossTranslationUnitContext &CTU) { 143*e8d8bef9SDimitry Andric C.emplace_back(new TextDiagnostics(std::move(DiagOpts), PP.getDiagnostics(), 144*e8d8bef9SDimitry Andric PP.getLangOpts(), 145*e8d8bef9SDimitry Andric /*ShouldDisplayPathNotes=*/true)); 1465ffd83dbSDimitry Andric } 1475ffd83dbSDimitry Andric 1485ffd83dbSDimitry Andric void ento::createTextMinimalPathDiagnosticConsumer( 149*e8d8bef9SDimitry Andric PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C, 1505ffd83dbSDimitry Andric const std::string &Prefix, const clang::Preprocessor &PP, 1515ffd83dbSDimitry Andric const cross_tu::CrossTranslationUnitContext &CTU) { 152*e8d8bef9SDimitry Andric C.emplace_back(new TextDiagnostics(std::move(DiagOpts), PP.getDiagnostics(), 153*e8d8bef9SDimitry Andric PP.getLangOpts(), 154*e8d8bef9SDimitry Andric /*ShouldDisplayPathNotes=*/false)); 1555ffd83dbSDimitry Andric } 156