clang 24.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"
14#include "HTMLDiagnostics.h"
18#include "clang/Basic/Sarif.h"
20#include "clang/Basic/Version.h"
22#include "clang/Lex/Lexer.h"
25#include "llvm/ADT/StringMap.h"
26#include "llvm/Support/ConvertUTF.h"
27#include "llvm/Support/JSON.h"
28#include <memory>
29
30using namespace llvm;
31using namespace clang;
32using namespace ento;
33
34namespace {
35class SarifDiagnostics : public PathDiagnosticConsumer {
36 std::string OutputFile;
37 const LangOptions &LO;
38 const SourceManager &SM;
39 SarifDocumentWriter SarifWriter;
40
41public:
42 SarifDiagnostics(const std::string &Output, const LangOptions &LO,
43 const SourceManager &SM)
44 : OutputFile(Output), LO(LO), SM(SM), SarifWriter(SM) {}
45 ~SarifDiagnostics() override = default;
46
47 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
48 FilesMade *FM) override;
49
50 StringRef getName() const override { return "SarifDiagnostics"; }
51 PathGenerationScheme getGenerationScheme() const override { return Minimal; }
52 bool supportsLogicalOpControlFlow() const override { return true; }
53 bool supportsCrossFileDiagnostics() const override { return true; }
54
55private:
56 SarifResult createResult(const PathDiagnostic *Diag,
57 const StringMap<uint32_t> &RuleMapping,
58 const LangOptions &LO, FilesMade *FM);
59};
60} // end anonymous namespace
61
62void ento::createSarifDiagnosticConsumer(
64 const std::string &Output, const Preprocessor &PP,
66 const MacroExpansionContext &MacroExpansions) {
67
68 createSarifDiagnosticConsumerImpl(DiagOpts, C, Output, PP);
69
70 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, Output, PP,
71 CTU, MacroExpansions);
72}
73
74/// Creates and registers a SARIF diagnostic consumer, without any additional
75/// text consumer.
78 const std::string &Output, const Preprocessor &PP) {
79
80 // TODO: Emit an error here.
81 if (Output.empty())
82 return;
83
84 C.push_back(std::make_unique<SarifDiagnostics>(Output, PP.getLangOpts(),
85 PP.getSourceManager()));
86}
87
88static StringRef getRuleDescription(StringRef CheckName) {
89 return llvm::StringSwitch<StringRef>(CheckName)
90#define GET_CHECKERS
91#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
92 .Case(FULLNAME, HELPTEXT)
93#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
94#undef CHECKER
95#undef GET_CHECKERS
96 ;
97}
98
99static StringRef getRuleHelpURIStr(StringRef CheckName) {
100 return llvm::StringSwitch<StringRef>(CheckName)
101#define GET_CHECKERS
102#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
103 .Case(FULLNAME, DOC_URI)
104#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
105#undef CHECKER
106#undef GET_CHECKERS
107 ;
108}
109
112 switch (Piece.getKind()) {
117 // FIXME: What should be reported here?
118 break;
120 return Piece.getTagStr() == "ConditionBRVisitor"
125 }
127}
128
129/// Returns the character range to report for \p Loc.
130///
131/// A thread flow needs a location for every piece, so an unusable range falls
132/// back to a caret rather than being dropped, which would truncate the path.
134 const LangOptions &LO) {
135 const SourceManager &SM = Loc.getManager();
136 FullSourceLoc Caret = Loc.asLocation().getExpansionLoc();
137 SourceRange Range = Loc.asRange();
138
139 // FIXME: A single-token range is reported as a zero-width region. Widening it
140 // would churn every expected-sarif file, so it is left alone for now.
141 if (Range.getBegin() != Range.getEnd()) {
142 if (std::optional<CharSourceRange> FileRange = getExpansionRangeInFile(
144 return Lexer::getAsCharRange(*FileRange, SM, LO);
145 }
146
147 return CharSourceRange::getCharRange(Caret, Caret);
148}
149
151 const LangOptions &LO) {
153 const PathPieces &Pieces = Diag->path.flatten(false);
154 for (const auto &Piece : Pieces) {
155 auto Flow = ThreadFlow::create()
157 .setRange(getDisplayCharRange(Piece->getLocation(), LO))
158 .setMessage(Piece->getString());
159 Flows.push_back(Flow);
160 }
161 return Flows;
162}
163
164static StringMap<uint32_t>
165createRuleMapping(const std::vector<const PathDiagnostic *> &Diags,
166 SarifDocumentWriter &SarifWriter) {
167 StringMap<uint32_t> RuleMapping;
168 llvm::StringSet<> Seen;
169
170 for (const PathDiagnostic *D : Diags) {
171 StringRef CheckName = D->getCheckerName();
172 std::pair<llvm::StringSet<>::iterator, bool> P = Seen.insert(CheckName);
173 if (P.second) {
174 auto Rule = SarifRule::create()
175 .setName(CheckName)
176 .setRuleId(CheckName)
178 .setHelpURI(getRuleHelpURIStr(CheckName));
179 size_t RuleIdx = SarifWriter.createRule(Rule);
180 RuleMapping[CheckName] = RuleIdx;
181 }
182 }
183 return RuleMapping;
184}
185
186static const llvm::StringRef IssueHashKey = "clang/issueHash/v1";
187
189SarifDiagnostics::createResult(const PathDiagnostic *Diag,
190 const StringMap<uint32_t> &RuleMapping,
191 const LangOptions &LO, FilesMade *FM) {
192
193 StringRef CheckName = Diag->getCheckerName();
194 uint32_t RuleIdx = RuleMapping.lookup(CheckName);
195 CharSourceRange Range = getDisplayCharRange(Diag->getLocation(), LO);
196
198
199 auto IssueHash = Diag->getIssueHash(SM, LO);
200
201 std::string HtmlReportURL;
202 if (FM && !FM->empty()) {
203 // Find the HTML report that was generated for this issue, if one exists.
204 PDFileEntry::ConsumerFiles *Files = FM->getFiles(*Diag);
205 if (Files) {
206 auto HtmlFile = llvm::find_if(*Files, [](const auto &File) {
207 return File.first == HTML_DIAGNOSTICS_NAME;
208 });
209 if (HtmlFile != Files->end()) {
210 SmallString<128> HtmlReportPath =
211 llvm::sys::path::parent_path(OutputFile);
212 llvm::sys::path::append(HtmlReportPath, HtmlFile->second);
213 HtmlReportURL = SarifDocumentWriter::fileNameToURI(HtmlReportPath);
214 }
215 }
216 }
217
218 auto Result = SarifResult::create(RuleIdx)
219 .setRuleId(CheckName)
220 .setDiagnosticMessage(Diag->getVerboseDescription())
223 .addPartialFingerprint(IssueHashKey, IssueHash)
224 .setHostedViewerURI(HtmlReportURL)
225 .setThreadFlows(Flows);
226 return Result;
227}
228
229void SarifDiagnostics::FlushDiagnosticsImpl(
230 std::vector<const PathDiagnostic *> &Diags, FilesMade *FM) {
231 // We currently overwrite the file if it already exists. However, it may be
232 // useful to add a feature someday that allows the user to append a run to an
233 // existing SARIF file. One danger from that approach is that the size of the
234 // file can become large very quickly, so decoding into JSON to append a run
235 // may be an expensive operation.
236 std::error_code EC;
237 llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
238 if (EC) {
239 llvm::errs() << "warning: could not create file: " << EC.message() << '\n';
240 return;
241 }
242
243 std::string ToolVersion = getClangFullVersion();
244 SarifWriter.createRun("clang", "clang static analyzer", ToolVersion);
245 StringMap<uint32_t> RuleMapping = createRuleMapping(Diags, SarifWriter);
246 for (const PathDiagnostic *D : Diags) {
247 SarifResult Result = createResult(D, RuleMapping, LO, FM);
248 SarifWriter.appendResult(Result);
249 }
250 auto Document = SarifWriter.createDocument();
251 OS << llvm::formatv("{0:2}\n", json::Value(std::move(Document)));
252}
#define HTML_DIAGNOSTICS_NAME
Result
Implement __builtin_bit_cast and related operations.
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 StringRef getRuleHelpURIStr(StringRef CheckName)
static StringRef getRuleDescription(StringRef CheckName)
static StringMap< uint32_t > createRuleMapping(const std::vector< const PathDiagnostic * > &Diags, SarifDocumentWriter &SarifWriter)
static const llvm::StringRef IssueHashKey
static ThreadFlowImportance calculateImportance(const PathDiagnosticPiece &Piece)
static CharSourceRange getDisplayCharRange(const PathDiagnosticLocation &Loc, const LangOptions &LO)
Returns the character range to report for Loc.
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 byte-granular source range.
static CharSourceRange getCharRange(SourceRange R)
static CharSourceRange getTokenRange(SourceRange R)
A SourceLocation and its associated SourceManager.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
static CharSourceRange getAsCharRange(SourceRange Range, const SourceManager &SM, const LangOptions &LangOpts)
Given a token range, produce a corresponding CharSourceRange that is not a token range.
Definition Lexer.h:438
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:414
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:345
size_t createRule(const SarifRule &Rule)
Associate the given rule with the current run.
Definition Sarif.cpp:381
static std::string fileNameToURI(llvm::StringRef Filename)
Definition Sarif.cpp:70
llvm::json::Object createDocument()
Return the SARIF document in its current state.
Definition Sarif.cpp:437
void appendResult(const SarifResult &SarifResult)
Append a new result to the currently in-flight run.
Definition Sarif.cpp:387
A SARIF result (also called a "reporting item") is a unit of output produced when one of the tool's r...
Definition Sarif.h:322
SarifResult setHostedViewerURI(llvm::StringRef URI)
Definition Sarif.h:360
SarifResult setThreadFlows(llvm::ArrayRef< ThreadFlow > ThreadFlowResults)
Definition Sarif.h:388
SarifResult setDiagnosticMessage(llvm::StringRef Message)
Definition Sarif.h:355
SarifResult setRuleId(llvm::StringRef Id)
Definition Sarif.h:350
SarifResult addLocations(llvm::ArrayRef< CharSourceRange > DiagLocs)
Definition Sarif.h:365
SarifResult setDiagnosticLevel(const SarifResultLevel &TheLevel)
Definition Sarif.h:393
static SarifResult create(uint32_t RuleIdx)
Definition Sarif.h:343
SarifRule setDescription(llvm::StringRef RuleDesc)
Definition Sarif.h:282
SarifRule setHelpURI(llvm::StringRef RuleHelpURI)
Definition Sarif.h:287
SarifRule setRuleId(llvm::StringRef RuleId)
Definition Sarif.h:277
static SarifRule create()
Definition Sarif.h:270
SarifRule setName(llvm::StringRef RuleName)
Definition Sarif.h:272
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
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
Top level wrappers for InstallAPI frontend operations.
ThreadFlowImportance
Definition Sarif.h:146
std::optional< CharSourceRange > getExpansionRangeInFile(CharSourceRange Range, FileID FID, const SourceManager &SM)
Maps both endpoints of Range to their macro expansion, so that the range can be shown to a user.
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
__packed_splat4 __packed_splat2 __packed_splat8 __packed_splat4 __packed_splat2 __packed_splat4 __packed_splat2 __packed_splat8 __packed_splat4 uint32_t
These options tweak the behavior of path diangostic consumers.