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/FileSystem.h"
28#include "llvm/Support/JSON.h"
29#include "llvm/Support/Path.h"
30#include <memory>
31
32using namespace llvm;
33using namespace clang;
34using namespace ento;
35
36namespace {
37class SarifDiagnostics : public PathDiagnosticConsumer {
38 std::string OutputFile;
39 const LangOptions &LO;
40 const SourceManager &SM;
41 SarifDocumentWriter SarifWriter;
42
43public:
44 SarifDiagnostics(const std::string &Output, const LangOptions &LO,
45 const SourceManager &SM)
46 : OutputFile(Output), LO(LO), SM(SM), SarifWriter(SM) {}
47 ~SarifDiagnostics() override = default;
48
49 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
50 FilesMade *FM) override;
51
52 StringRef getName() const override { return "SarifDiagnostics"; }
53 PathGenerationScheme getGenerationScheme() const override { return Minimal; }
54 bool supportsLogicalOpControlFlow() const override { return true; }
55 bool supportsCrossFileDiagnostics() const override { return true; }
56
57private:
58 SarifResult createResult(const PathDiagnostic *Diag,
59 const StringMap<uint32_t> &RuleMapping,
60 const LangOptions &LO, FilesMade *FM);
61};
62} // end anonymous namespace
63
64void ento::createSarifDiagnosticConsumer(
66 const std::string &Output, const Preprocessor &PP,
68 const MacroExpansionContext &MacroExpansions) {
69
70 createSarifDiagnosticConsumerImpl(DiagOpts, C, Output, PP);
71
72 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, Output, PP,
73 CTU, MacroExpansions);
74}
75
76/// Creates and registers a SARIF diagnostic consumer, without any additional
77/// text consumer.
80 const std::string &Output, const Preprocessor &PP) {
81
82 // TODO: Emit an error here.
83 if (Output.empty())
84 return;
85
86 C.push_back(std::make_unique<SarifDiagnostics>(Output, PP.getLangOpts(),
87 PP.getSourceManager()));
88}
89
90static StringRef getRuleDescription(StringRef CheckName) {
91 return llvm::StringSwitch<StringRef>(CheckName)
92#define GET_CHECKERS
93#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
94 .Case(FULLNAME, HELPTEXT)
95#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
96#undef CHECKER
97#undef GET_CHECKERS
98 ;
99}
100
101static StringRef getRuleHelpURIStr(StringRef CheckName) {
102 return llvm::StringSwitch<StringRef>(CheckName)
103#define GET_CHECKERS
104#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
105 .Case(FULLNAME, DOC_URI)
106#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
107#undef CHECKER
108#undef GET_CHECKERS
109 ;
110}
111
114 switch (Piece.getKind()) {
119 // FIXME: What should be reported here?
120 break;
122 return Piece.getTagStr() == "ConditionBRVisitor"
127 }
129}
130
131/// Returns the character range to report for \p Loc.
132///
133/// A thread flow needs a location for every piece, so an unusable range falls
134/// back to a caret rather than being dropped, which would truncate the path.
136 const LangOptions &LO) {
137 const SourceManager &SM = Loc.getManager();
138 FullSourceLoc Caret = Loc.asLocation().getExpansionLoc();
139 SourceRange Range = Loc.asRange();
140
141 // FIXME: A single-token range is reported as a zero-width region. Widening it
142 // would churn every expected-sarif file, so it is left alone for now.
143 if (Range.getBegin() != Range.getEnd()) {
144 if (std::optional<CharSourceRange> FileRange = getExpansionRangeInFile(
146 return Lexer::getAsCharRange(*FileRange, SM, LO);
147 }
148
149 return CharSourceRange::getCharRange(Caret, Caret);
150}
151
153 const LangOptions &LO) {
155 const PathPieces &Pieces = Diag->path.flatten(false);
156 for (const auto &Piece : Pieces) {
157 auto Flow = ThreadFlow::create()
159 .setRange(getDisplayCharRange(Piece->getLocation(), LO))
160 .setMessage(Piece->getString());
161 Flows.push_back(Flow);
162 }
163 return Flows;
164}
165
166static StringMap<uint32_t>
167createRuleMapping(const std::vector<const PathDiagnostic *> &Diags,
168 SarifDocumentWriter &SarifWriter) {
169 StringMap<uint32_t> RuleMapping;
170 llvm::StringSet<> Seen;
171
172 for (const PathDiagnostic *D : Diags) {
173 StringRef CheckName = D->getCheckerName();
174 std::pair<llvm::StringSet<>::iterator, bool> P = Seen.insert(CheckName);
175 if (P.second) {
176 auto Rule = SarifRule::create()
177 .setName(CheckName)
178 .setRuleId(CheckName)
180 .setHelpURI(getRuleHelpURIStr(CheckName));
181 size_t RuleIdx = SarifWriter.createRule(Rule);
182 RuleMapping[CheckName] = RuleIdx;
183 }
184 }
185 return RuleMapping;
186}
187
188static const llvm::StringRef IssueHashKey = "clang/issueHash/v1";
189
191SarifDiagnostics::createResult(const PathDiagnostic *Diag,
192 const StringMap<uint32_t> &RuleMapping,
193 const LangOptions &LO, FilesMade *FM) {
194
195 StringRef CheckName = Diag->getCheckerName();
196 uint32_t RuleIdx = RuleMapping.lookup(CheckName);
197 CharSourceRange Range = getDisplayCharRange(Diag->getLocation(), LO);
198
200
201 auto IssueHash = Diag->getIssueHash(SM, LO);
202
203 std::string HtmlReportURL;
204 if (FM && !FM->empty()) {
205 // Find the HTML report that was generated for this issue, if one exists.
206 PDFileEntry::ConsumerFiles *Files = FM->getFiles(*Diag);
207 if (Files) {
208 auto HtmlFile = llvm::find_if(*Files, [](const auto &File) {
209 return File.first == HTML_DIAGNOSTICS_NAME;
210 });
211 if (HtmlFile != Files->end()) {
212 SmallString<128> HtmlReportPath =
213 llvm::sys::path::parent_path(OutputFile);
214 llvm::sys::path::append(HtmlReportPath, HtmlFile->second);
215 HtmlReportURL = SarifDocumentWriter::fileNameToURI(HtmlReportPath);
216 }
217 }
218 }
219
220 auto Result = SarifResult::create(RuleIdx)
221 .setRuleId(CheckName)
222 .setDiagnosticMessage(Diag->getVerboseDescription())
225 .addPartialFingerprint(IssueHashKey, IssueHash)
226 .setHostedViewerURI(HtmlReportURL)
227 .setThreadFlows(Flows);
228 return Result;
229}
230
231void SarifDiagnostics::FlushDiagnosticsImpl(
232 std::vector<const PathDiagnostic *> &Diags, FilesMade *FM) {
233 // We currently overwrite the file if it already exists. However, it may be
234 // useful to add a feature someday that allows the user to append a run to an
235 // existing SARIF file. One danger from that approach is that the size of the
236 // file can become large very quickly, so decoding into JSON to append a run
237 // may be an expensive operation.
238 std::error_code EC;
239 llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
240 if (EC) {
241 llvm::errs() << "warning: could not create file: " << EC.message() << '\n';
242 return;
243 }
244
245 std::string ToolVersion = getClangFullVersion();
246 SarifWriter.createRun("clang", "clang static analyzer", ToolVersion);
247 StringMap<uint32_t> RuleMapping = createRuleMapping(Diags, SarifWriter);
248 for (const PathDiagnostic *D : Diags) {
249 SarifResult Result = createResult(D, RuleMapping, LO, FM);
250 SarifWriter.appendResult(Result);
251 }
252 auto Document = SarifWriter.createDocument();
253 OS << llvm::formatv("{0:2}\n", json::Value(std::move(Document)));
254}
#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:440
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.