clang 22.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
13#include "SarifDiagnostics.h"
16#include "clang/Basic/Sarif.h"
18#include "clang/Basic/Version.h"
21#include "llvm/ADT/StringMap.h"
22#include "llvm/Support/ConvertUTF.h"
23#include "llvm/Support/JSON.h"
24#include <memory>
25
26using namespace llvm;
27using namespace clang;
28using namespace ento;
29
30namespace {
31class SarifDiagnostics : public PathDiagnosticConsumer {
32 std::string OutputFile;
33 const LangOptions &LO;
34 SarifDocumentWriter SarifWriter;
35
36public:
37 SarifDiagnostics(const std::string &Output, const LangOptions &LO,
38 const SourceManager &SM)
39 : OutputFile(Output), LO(LO), SarifWriter(SM) {}
40 ~SarifDiagnostics() override = default;
41
42 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
43 FilesMade *FM) override;
44
45 StringRef getName() const override { return "SarifDiagnostics"; }
46 PathGenerationScheme getGenerationScheme() const override { return Minimal; }
47 bool supportsLogicalOpControlFlow() const override { return true; }
48 bool supportsCrossFileDiagnostics() const override { return true; }
49};
50} // end anonymous namespace
51
52void ento::createSarifDiagnosticConsumer(
54 const std::string &Output, const Preprocessor &PP,
56 const MacroExpansionContext &MacroExpansions) {
57
58 createSarifDiagnosticConsumerImpl(DiagOpts, C, Output, PP);
59
60 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, Output, PP,
61 CTU, MacroExpansions);
62}
63
64/// Creates and registers a SARIF diagnostic consumer, without any additional
65/// text consumer.
68 const std::string &Output, const Preprocessor &PP) {
69
70 // TODO: Emit an error here.
71 if (Output.empty())
72 return;
73
74 C.push_back(std::make_unique<SarifDiagnostics>(Output, PP.getLangOpts(),
75 PP.getSourceManager()));
76}
77
78static StringRef getRuleDescription(StringRef CheckName) {
79 return llvm::StringSwitch<StringRef>(CheckName)
80#define GET_CHECKERS
81#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
82 .Case(FULLNAME, HELPTEXT)
83#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
84#undef CHECKER
85#undef GET_CHECKERS
86 ;
87}
88
89static StringRef getRuleHelpURIStr(StringRef CheckName) {
90 return llvm::StringSwitch<StringRef>(CheckName)
91#define GET_CHECKERS
92#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
93 .Case(FULLNAME, DOC_URI)
94#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
95#undef CHECKER
96#undef GET_CHECKERS
97 ;
98}
99
102 switch (Piece.getKind()) {
107 // FIXME: What should be reported here?
108 break;
110 return Piece.getTagStr() == "ConditionBRVisitor"
115 }
117}
118
119/// Accepts a SourceRange corresponding to a pair of the first and last tokens
120/// and converts to a Character granular CharSourceRange.
122 const SourceManager &SM,
123 const LangOptions &LO) {
124 // Caret diagnostics have the first and last locations pointed at the same
125 // location, return these as-is.
126 if (R.getBegin() == R.getEnd())
128
129 SourceLocation BeginCharLoc = R.getBegin();
130 // For token ranges, the raw end SLoc points at the first character of the
131 // last token in the range. This must be moved to one past the end of the
132 // last character using the lexer.
133 SourceLocation EndCharLoc =
134 Lexer::getLocForEndOfToken(R.getEnd(), /* Offset = */ 0, SM, LO);
135 return CharSourceRange::getCharRange(BeginCharLoc, EndCharLoc);
136}
137
139 const LangOptions &LO) {
141 const PathPieces &Pieces = Diag->path.flatten(false);
142 for (const auto &Piece : Pieces) {
144 Piece->getLocation().asRange(), Piece->getLocation().getManager(), LO);
145 auto Flow = ThreadFlow::create()
148 .setMessage(Piece->getString());
149 Flows.push_back(Flow);
150 }
151 return Flows;
152}
153
154static StringMap<uint32_t>
155createRuleMapping(const std::vector<const PathDiagnostic *> &Diags,
156 SarifDocumentWriter &SarifWriter) {
157 StringMap<uint32_t> RuleMapping;
158 llvm::StringSet<> Seen;
159
160 for (const PathDiagnostic *D : Diags) {
161 StringRef CheckName = D->getCheckerName();
162 std::pair<llvm::StringSet<>::iterator, bool> P = Seen.insert(CheckName);
163 if (P.second) {
164 auto Rule = SarifRule::create()
165 .setName(CheckName)
166 .setRuleId(CheckName)
168 .setHelpURI(getRuleHelpURIStr(CheckName));
169 size_t RuleIdx = SarifWriter.createRule(Rule);
170 RuleMapping[CheckName] = RuleIdx;
171 }
172 }
173 return RuleMapping;
174}
175
177 const StringMap<uint32_t> &RuleMapping,
178 const LangOptions &LO) {
179
180 StringRef CheckName = Diag->getCheckerName();
181 uint32_t RuleIdx = RuleMapping.lookup(CheckName);
183 Diag->getLocation().asRange(), Diag->getLocation().getManager(), LO);
184
186 auto Result = SarifResult::create(RuleIdx)
187 .setRuleId(CheckName)
188 .setDiagnosticMessage(Diag->getVerboseDescription())
191 .setThreadFlows(Flows);
192 return Result;
193}
194
195void SarifDiagnostics::FlushDiagnosticsImpl(
196 std::vector<const PathDiagnostic *> &Diags, FilesMade *) {
197 // We currently overwrite the file if it already exists. However, it may be
198 // useful to add a feature someday that allows the user to append a run to an
199 // existing SARIF file. One danger from that approach is that the size of the
200 // file can become large very quickly, so decoding into JSON to append a run
201 // may be an expensive operation.
202 std::error_code EC;
203 llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
204 if (EC) {
205 llvm::errs() << "warning: could not create file: " << EC.message() << '\n';
206 return;
207 }
208
209 std::string ToolVersion = getClangFullVersion();
210 SarifWriter.createRun("clang", "clang static analyzer", ToolVersion);
211 StringMap<uint32_t> RuleMapping = createRuleMapping(Diags, SarifWriter);
212 for (const PathDiagnostic *D : Diags) {
213 SarifResult Result = createResult(D, RuleMapping, LO);
214 SarifWriter.appendResult(Result);
215 }
216 auto Document = SarifWriter.createDocument();
217 OS << llvm::formatv("{0:2}\n", json::Value(std::move(Document)));
218}
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.
#define SM(sm)
Defines the clang::Preprocessor interface.
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 StringMap< uint32_t > createRuleMapping(const std::vector< const PathDiagnostic * > &Diags, SarifDocumentWriter &SarifWriter)
static ThreadFlowImportance calculateImportance(const PathDiagnosticPiece &Piece)
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.
static CharSourceRange getCharRange(SourceRange R)
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
static SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &LangOpts)
Computes the source location just past the end of the token at this source location.
Definition Lexer.cpp:848
MacroExpansionContext tracks the macro expansions processed by the Preprocessor.
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
SourceManager & getSourceManager() const
const LangOptions & getLangOpts() const
This class handles creating a valid SARIF document given various input attributes.
Definition Sarif.h:380
void createRun(const llvm::StringRef ShortToolName, const llvm::StringRef LongToolName, const llvm::StringRef ToolVersion=CLANG_VERSION_STRING)
Create a new run with which any upcoming analysis will be associated.
Definition Sarif.cpp:342
size_t createRule(const SarifRule &Rule)
Associate the given rule with the current run.
Definition Sarif.cpp:378
llvm::json::Object createDocument()
Return the SARIF document in its current state.
Definition Sarif.cpp:412
void appendResult(const SarifResult &SarifResult)
Append a new result to the currently in-flight run.
Definition Sarif.cpp:384
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
SarifResult setDiagnosticMessage(llvm::StringRef Message)
Definition Sarif.h:345
SarifResult setRuleId(llvm::StringRef Id)
Definition Sarif.h:340
SarifResult setLocations(llvm::ArrayRef< CharSourceRange > DiagLocs)
Definition Sarif.h:350
SarifResult setDiagnosticLevel(const SarifResultLevel &TheLevel)
Definition Sarif.h:365
static SarifResult create(uint32_t RuleIdx)
Definition Sarif.h:333
SarifRule setDescription(llvm::StringRef RuleDesc)
Definition Sarif.h:281
SarifRule setHelpURI(llvm::StringRef RuleHelpURI)
Definition Sarif.h:286
SarifRule setRuleId(llvm::StringRef RuleId)
Definition Sarif.h:276
static SarifRule create()
Definition Sarif.h:269
SarifRule setName(llvm::StringRef RuleName)
Definition Sarif.h:271
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
ThreadFlow setImportance(const ThreadFlowImportance &ItemImportance)
Definition Sarif.h:194
ThreadFlow setRange(const CharSourceRange &ItemRange)
Definition Sarif.h:187
static ThreadFlow create()
Definition Sarif.h:185
ThreadFlow setMessage(llvm::StringRef ItemMessage)
Definition Sarif.h:199
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< std::unique_ptr< PathDiagnosticConsumer > > PathDiagnosticConsumers
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
void createSarifDiagnosticConsumerImpl(PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C, const std::string &Output, const Preprocessor &PP)
Creates and registers a SARIF diagnostic consumer, without any additional text consumer.
StringRef getName(const HeaderType T)
Definition HeaderFile.h:38
The JSON file list parser is used to communicate input to InstallAPI.
ThreadFlowImportance
Definition Sarif.h:146
@ Result
The result type of a method or function.
Definition TypeBase.h:905
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.