clang  19.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(
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 
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 
130  const LangOptions &LO) {
132  const PathPieces &Pieces = Diag->path.flatten(false);
133  for (const auto &Piece : Pieces) {
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 
168  const StringMap<uint32_t> &RuleMapping,
169  const LangOptions &LO) {
170 
171  StringRef CheckName = Diag->getCheckerName();
172  uint32_t RuleIdx = RuleMapping.lookup(CheckName);
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 }
StringRef P
#define SM(sm)
Definition: Cuda.cpp:83
Defines the clang::FileManager interface and associated types.
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.
Defines the clang::Preprocessor interface.
static std::string getName(const CallEvent &Call)
static StringRef getRuleHelpURIStr(StringRef CheckName)
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...
static StringRef getRuleDescription(StringRef CheckName)
static SarifResult createResult(const PathDiagnostic *Diag, const StringMap< uint32_t > &RuleMapping, const LangOptions &LO)
static ThreadFlowImportance calculateImportance(const PathDiagnosticPiece &Piece)
static StringMap< uint32_t > createRuleMapping(const std::vector< const PathDiagnostic * > &Diags, SarifDocumentWriter &SarifWriter)
static SmallVector< ThreadFlow, 8 > createThreadFlows(const PathDiagnostic *Diag, const LangOptions &LO)
Defines clang::SarifDocumentWriter, clang::SarifRule, clang::SarifResult.
Defines the SourceManager interface.
Defines version macros and version-related utility functions for Clang.
Represents a character-granular source range.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:482
MacroExpansionContext tracks the macro expansions processed by the Preprocessor.
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:128
SourceManager & getSourceManager() const
const LangOptions & getLangOpts() const
This class handles creating a valid SARIF document given various input attributes.
Definition: Sarif.h:380
size_t createRule(const SarifRule &Rule)
Associate the given rule with the current run.
Definition: Sarif.cpp:379
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
Encodes a location in the source.
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
SourceLocation getBegin() const
This class is used for tools that requires cross translation unit capability.
StringRef getTagStr() const
Return the string representation of the tag.
PathDiagnostic - PathDiagnostic objects represent a single path-sensitive diagnostic.
A Range represents the closed range [from, to].
std::vector< PathDiagnosticConsumer * > PathDiagnosticConsumers
bool Call(InterpState &S, CodePtr OpPC, const Function *Func, uint32_t VarArgSize)
Definition: Interp.h:2179
std::unique_ptr< DiagnosticConsumer > create(StringRef OutputFile, DiagnosticOptions *Diags, bool MergeChildRecords=false)
Returns a DiagnosticConsumer that serializes diagnostics to a bitcode file.
The JSON file list parser is used to communicate input to InstallAPI.
ThreadFlowImportance
Definition: Sarif.h:146
std::string getClangFullVersion()
Retrieves a string representing the complete clang version, which includes the clang version number,...
Definition: Version.cpp:96
Diagnostic wrappers for TextAPI types for error reporting.
Definition: Dominators.h:30
These options tweak the behavior of path diangostic consumers.