clang  16.0.0git
SarifDiagnostics.cpp
Go to the documentation of this file.
1 //===--- SarifDiagnostics.cpp - Sarif 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 SarifDiagnostics object.
10 //
11 //===----------------------------------------------------------------------===//
12 
16 #include "clang/Basic/Sarif.h"
18 #include "clang/Basic/Version.h"
19 #include "clang/Lex/Preprocessor.h"
21 #include "llvm/ADT/STLExtras.h"
22 #include "llvm/ADT/StringMap.h"
23 #include "llvm/Support/ConvertUTF.h"
24 #include "llvm/Support/JSON.h"
25 #include "llvm/Support/Path.h"
26 
27 using namespace llvm;
28 using namespace clang;
29 using namespace ento;
30 
31 namespace {
32 class SarifDiagnostics : public PathDiagnosticConsumer {
33  std::string OutputFile;
34  const LangOptions &LO;
35  SarifDocumentWriter SarifWriter;
36 
37 public:
38  SarifDiagnostics(const std::string &Output, const LangOptions &LO,
39  const SourceManager &SM)
40  : OutputFile(Output), LO(LO), SarifWriter(SM) {}
41  ~SarifDiagnostics() override = default;
42 
43  void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
44  FilesMade *FM) override;
45 
46  StringRef getName() const override { return "SarifDiagnostics"; }
47  PathGenerationScheme getGenerationScheme() const override { return Minimal; }
48  bool supportsLogicalOpControlFlow() const override { return true; }
49  bool supportsCrossFileDiagnostics() const override { return true; }
50 };
51 } // end anonymous namespace
52 
53 void ento::createSarifDiagnosticConsumer(
54  PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
55  const std::string &Output, const Preprocessor &PP,
57  const MacroExpansionContext &MacroExpansions) {
58 
59  // TODO: Emit an error here.
60  if (Output.empty())
61  return;
62 
63  C.push_back(
64  new SarifDiagnostics(Output, PP.getLangOpts(), PP.getSourceManager()));
65  createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, Output, PP,
66  CTU, MacroExpansions);
67 }
68 
69 static StringRef getRuleDescription(StringRef CheckName) {
70  return llvm::StringSwitch<StringRef>(CheckName)
71 #define GET_CHECKERS
72 #define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
73  .Case(FULLNAME, HELPTEXT)
74 #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
75 #undef CHECKER
76 #undef GET_CHECKERS
77  ;
78 }
79 
80 static StringRef getRuleHelpURIStr(StringRef CheckName) {
81  return llvm::StringSwitch<StringRef>(CheckName)
82 #define GET_CHECKERS
83 #define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
84  .Case(FULLNAME, DOC_URI)
85 #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
86 #undef CHECKER
87 #undef GET_CHECKERS
88  ;
89 }
90 
92 calculateImportance(const PathDiagnosticPiece &Piece) {
93  switch (Piece.getKind()) {
95  case PathDiagnosticPiece::Macro:
97  case PathDiagnosticPiece::PopUp:
98  // FIXME: What should be reported here?
99  break;
100  case PathDiagnosticPiece::Event:
101  return Piece.getTagStr() == "ConditionBRVisitor"
102  ? ThreadFlowImportance::Important
103  : ThreadFlowImportance::Essential;
104  case PathDiagnosticPiece::ControlFlow:
105  return ThreadFlowImportance::Unimportant;
106  }
107  return ThreadFlowImportance::Unimportant;
108 }
109 
110 /// Accepts a SourceRange corresponding to a pair of the first and last tokens
111 /// and converts to a Character granular CharSourceRange.
113  const SourceManager &SM,
114  const LangOptions &LO) {
115  // Caret diagnostics have the first and last locations pointed at the same
116  // location, return these as-is.
117  if (R.getBegin() == R.getEnd())
118  return CharSourceRange::getCharRange(R);
119 
120  SourceLocation BeginCharLoc = R.getBegin();
121  // For token ranges, the raw end SLoc points at the first character of the
122  // last token in the range. This must be moved to one past the end of the
123  // last character using the lexer.
124  SourceLocation EndCharLoc =
125  Lexer::getLocForEndOfToken(R.getEnd(), /* Offset = */ 0, SM, LO);
126  return CharSourceRange::getCharRange(BeginCharLoc, EndCharLoc);
127 }
128 
129 static SmallVector<ThreadFlow, 8> createThreadFlows(const PathDiagnostic *Diag,
130  const LangOptions &LO) {
132  const PathPieces &Pieces = Diag->path.flatten(false);
133  for (const auto &Piece : Pieces) {
134  auto Range = convertTokenRangeToCharRange(
135  Piece->getLocation().asRange(), Piece->getLocation().getManager(), LO);
136  auto Flow = ThreadFlow::create()
137  .setImportance(calculateImportance(*Piece))
138  .setRange(Range)
139  .setMessage(Piece->getString());
140  Flows.push_back(Flow);
141  }
142  return Flows;
143 }
144 
145 static StringMap<uint32_t>
146 createRuleMapping(const std::vector<const PathDiagnostic *> &Diags,
147  SarifDocumentWriter &SarifWriter) {
148  StringMap<uint32_t> RuleMapping;
149  llvm::StringSet<> Seen;
150 
151  for (const PathDiagnostic *D : Diags) {
152  StringRef CheckName = D->getCheckerName();
153  std::pair<llvm::StringSet<>::iterator, bool> P = Seen.insert(CheckName);
154  if (P.second) {
155  auto Rule = SarifRule::create()
156  .setName(CheckName)
157  .setRuleId(CheckName)
158  .setDescription(getRuleDescription(CheckName))
159  .setHelpURI(getRuleHelpURIStr(CheckName));
160  size_t RuleIdx = SarifWriter.createRule(Rule);
161  RuleMapping[CheckName] = RuleIdx;
162  }
163  }
164  return RuleMapping;
165 }
166 
167 static SarifResult createResult(const PathDiagnostic *Diag,
168  const StringMap<uint32_t> &RuleMapping,
169  const LangOptions &LO) {
170 
171  StringRef CheckName = Diag->getCheckerName();
172  uint32_t RuleIdx = RuleMapping.lookup(CheckName);
173  auto Range = convertTokenRangeToCharRange(
174  Diag->getLocation().asRange(), Diag->getLocation().getManager(), LO);
175 
177  auto Result = SarifResult::create(RuleIdx)
178  .setRuleId(CheckName)
179  .setDiagnosticMessage(Diag->getVerboseDescription())
180  .setDiagnosticLevel(SarifResultLevel::Warning)
181  .setLocations({Range})
182  .setThreadFlows(Flows);
183  return Result;
184 }
185 
186 void SarifDiagnostics::FlushDiagnosticsImpl(
187  std::vector<const PathDiagnostic *> &Diags, FilesMade *) {
188  // We currently overwrite the file if it already exists. However, it may be
189  // useful to add a feature someday that allows the user to append a run to an
190  // existing SARIF file. One danger from that approach is that the size of the
191  // file can become large very quickly, so decoding into JSON to append a run
192  // may be an expensive operation.
193  std::error_code EC;
194  llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
195  if (EC) {
196  llvm::errs() << "warning: could not create file: " << EC.message() << '\n';
197  return;
198  }
199 
200  std::string ToolVersion = getClangFullVersion();
201  SarifWriter.createRun("clang", "clang static analyzer", ToolVersion);
202  StringMap<uint32_t> RuleMapping = createRuleMapping(Diags, SarifWriter);
203  for (const PathDiagnostic *D : Diags) {
204  SarifResult Result = createResult(D, RuleMapping, LO);
205  SarifWriter.appendResult(Result);
206  }
207  auto Document = SarifWriter.createDocument();
208  OS << llvm::formatv("{0:2}\n", json::Value(std::move(Document)));
209 }
llvm
YAML serialization mapping.
Definition: Dominators.h:30
clang::SourceRange
A trivial tuple used to represent a source range.
Definition: SourceLocation.h:210
string
string(SUBSTRING ${CMAKE_CURRENT_BINARY_DIR} 0 ${PATH_LIB_START} PATH_HEAD) string(SUBSTRING $
Definition: CMakeLists.txt:22
Diag
static DiagnosticBuilder Diag(DiagnosticsEngine *Diags, const LangOptions &Features, FullSourceLoc TokLoc, const char *TokBegin, const char *TokRangeBegin, const char *TokRangeEnd, unsigned DiagID)
Produce a diagnostic highlighting some portion of a literal.
Definition: LiteralSupport.cpp:79
llvm::SmallVector
Definition: LLVM.h:38
clang::SourceLocation
Encodes a location in the source.
Definition: SourceLocation.h:86
clang::SourceRange::getBegin
SourceLocation getBegin() const
Definition: SourceLocation.h:219
PathDiagnosticConsumers.h
AttributeLangSupport::C
@ C
Definition: SemaDeclAttr.cpp:56
createRuleMapping
static StringMap< uint32_t > createRuleMapping(const std::vector< const PathDiagnostic * > &Diags, SarifDocumentWriter &SarifWriter)
Definition: SarifDiagnostics.cpp:146
SourceManager.h
clang::serialized_diags::Note
@ Note
Definition: SerializedDiagnostics.h:45
clang::SourceManager
This class handles loading and caching of source files into memory.
Definition: SourceManager.h:637
clang::Preprocessor::getLangOpts
const LangOptions & getLangOpts() const
Definition: Preprocessor.h:1061
Preprocessor.h
clang::SarifDocumentWriter::createRule
size_t createRule(const SarifRule &Rule)
Associate the given rule with the current run.
Definition: Sarif.cpp:376
clang::SarifResult
A SARIF result (also called a "reporting item") is a unit of output produced when one of the tool's r...
Definition: Sarif.h:315
clang::cross_tu::CrossTranslationUnitContext
This class is used for tools that requires cross translation unit capability.
Definition: CrossTranslationUnit.h:127
clang::SourceRange::getEnd
SourceLocation getEnd() const
Definition: SourceLocation.h:220
Version.h
getRuleHelpURIStr
static StringRef getRuleHelpURIStr(StringRef CheckName)
Definition: SarifDiagnostics.cpp:80
clang::serialized_diags::Warning
@ Warning
Definition: SerializedDiagnostics.h:46
createResult
static SarifResult createResult(const PathDiagnostic *Diag, const StringMap< uint32_t > &RuleMapping, const LangOptions &LO)
Definition: SarifDiagnostics.cpp:167
PathDiagnostic.h
getName
static std::string getName(const CallEvent &Call)
Definition: ReturnValueChecker.cpp:61
MacroExpansionContext.h
clang::Preprocessor::getSourceManager
SourceManager & getSourceManager() const
Definition: Preprocessor.h:1065
P
StringRef P
Definition: ASTMatchersInternal.cpp:564
clang::serialized_diags::create
std::unique_ptr< DiagnosticConsumer > create(StringRef OutputFile, DiagnosticOptions *Diags, bool MergeChildRecords=false)
Returns a DiagnosticConsumer that serializes diagnostics to a bitcode file.
Definition: SerializedDiagnosticPrinter.cpp:301
createThreadFlows
static SmallVector< ThreadFlow, 8 > createThreadFlows(const PathDiagnostic *Diag, const LangOptions &LO)
Definition: SarifDiagnostics.cpp:129
clang::CharSourceRange
Represents a character-granular source range.
Definition: SourceLocation.h:253
calculateImportance
static ThreadFlowImportance calculateImportance(const PathDiagnosticPiece &Piece)
Definition: SarifDiagnostics.cpp:92
clang::MacroExpansionContext
MacroExpansionContext tracks the macro expansions processed by the Preprocessor.
Definition: MacroExpansionContext.h:73
clang::LangOptions
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:81
clang::SarifDocumentWriter
This class handles creating a valid SARIF document given various input attributes.
Definition: Sarif.h:380
clang::ThreadFlowImportance
ThreadFlowImportance
Definition: Sarif.h:146
clang::ento::PathDiagnosticConsumers
std::vector< PathDiagnosticConsumer * > PathDiagnosticConsumers
Definition: PathDiagnosticConsumers.h:32
clang
Definition: CalledOnceCheck.h:17
clang::getClangFullVersion
std::string getClangFullVersion()
Retrieves a string representing the complete clang version, which includes the clang version number,...
Definition: Version.cpp:88
convertTokenRangeToCharRange
static CharSourceRange convertTokenRangeToCharRange(const SourceRange &R, const SourceManager &SM, const LangOptions &LO)
Accepts a SourceRange corresponding to a pair of the first and last tokens and converts to a Characte...
Definition: SarifDiagnostics.cpp:112
Sarif.h
getRuleDescription
static StringRef getRuleDescription(StringRef CheckName)
Definition: SarifDiagnostics.cpp:69
clang::StructuralEquivalenceKind::Minimal
@ Minimal
FileManager.h
clang::interp::Call
bool Call(InterpState &S, CodePtr &PC, const Function *Func)
Definition: Interp.h:1246
clang::Preprocessor
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:129
SM
#define SM(sm)
Definition: Cuda.cpp:79
clang::ento::ObjKind::OS
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...