TextDiagnostics.cpp 6.0 KB

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