clang 20.0.0 (based on r547379) from build 12806354. Bug: http://b/379133546 Test: N/A Change-Id: I2eb8938af55d809de674be63cb30cf27e801862b Upstream-Commit: ad834e67b1105d15ef907f6255d4c96e8e733f57
514 lines
18 KiB
C++
514 lines
18 KiB
C++
//== clang/Basic/Sarif.h - SARIF Diagnostics Object Model -------*- C++ -*--==//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
/// \file
|
|
/// Defines clang::SarifDocumentWriter, clang::SarifRule, clang::SarifResult.
|
|
///
|
|
/// The document built can be accessed as a JSON Object.
|
|
/// Several value semantic types are also introduced which represent properties
|
|
/// of the SARIF standard, such as 'artifact', 'result', 'rule'.
|
|
///
|
|
/// A SARIF (Static Analysis Results Interchange Format) document is JSON
|
|
/// document that describes in detail the results of running static analysis
|
|
/// tools on a project. Each (non-trivial) document consists of at least one
|
|
/// "run", which are themselves composed of details such as:
|
|
/// * Tool: The tool that was run
|
|
/// * Rules: The rules applied during the tool run, represented by
|
|
/// \c reportingDescriptor objects in SARIF
|
|
/// * Results: The matches for the rules applied against the project(s) being
|
|
/// evaluated, represented by \c result objects in SARIF
|
|
///
|
|
/// Reference:
|
|
/// 1. <a href="https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html">The SARIF standard</a>
|
|
/// 2. <a href="https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317836">SARIF<pre>reportingDescriptor</pre></a>
|
|
/// 3. <a href="https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317638">SARIF<pre>result</pre></a>
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_CLANG_BASIC_SARIF_H
|
|
#define LLVM_CLANG_BASIC_SARIF_H
|
|
|
|
#include "clang/Basic/SourceLocation.h"
|
|
#include "clang/Basic/Version.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/JSON.h"
|
|
#include <cassert>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <initializer_list>
|
|
#include <optional>
|
|
#include <string>
|
|
|
|
namespace clang {
|
|
|
|
class SarifDocumentWriter;
|
|
class SourceManager;
|
|
|
|
namespace detail {
|
|
|
|
/// \internal
|
|
/// An artifact location is SARIF's way of describing the complete location
|
|
/// of an artifact encountered during analysis. The \c artifactLocation object
|
|
/// typically consists of a URI, and/or an index to reference the artifact it
|
|
/// locates.
|
|
///
|
|
/// This builder makes an additional assumption: that every artifact encountered
|
|
/// by \c clang will be a physical, top-level artifact. Which is why the static
|
|
/// creation method \ref SarifArtifactLocation::create takes a mandatory URI
|
|
/// parameter. The official standard states that either a \c URI or \c Index
|
|
/// must be available in the object, \c clang picks the \c URI as a reasonable
|
|
/// default, because it intends to deal in physical artifacts for now.
|
|
///
|
|
/// Reference:
|
|
/// 1. <a href="https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317427">artifactLocation object</a>
|
|
/// 2. \ref SarifArtifact
|
|
class SarifArtifactLocation {
|
|
private:
|
|
friend class clang::SarifDocumentWriter;
|
|
|
|
std::optional<uint32_t> Index;
|
|
std::string URI;
|
|
|
|
SarifArtifactLocation() = delete;
|
|
explicit SarifArtifactLocation(const std::string &URI) : URI(URI) {}
|
|
|
|
public:
|
|
static SarifArtifactLocation create(llvm::StringRef URI) {
|
|
return SarifArtifactLocation{URI.str()};
|
|
}
|
|
|
|
SarifArtifactLocation setIndex(uint32_t Idx) {
|
|
Index = Idx;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
/// \internal
|
|
/// An artifact in SARIF is any object (a sequence of bytes) addressable by
|
|
/// a URI (RFC 3986). The most common type of artifact for clang's use-case
|
|
/// would be source files. SARIF's artifact object is described in detail in
|
|
/// section 3.24.
|
|
//
|
|
/// Since every clang artifact MUST have a location (there being no nested
|
|
/// artifacts), the creation method \ref SarifArtifact::create requires a
|
|
/// \ref SarifArtifactLocation object.
|
|
///
|
|
/// Reference:
|
|
/// 1. <a href="https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317611">artifact object</a>
|
|
class SarifArtifact {
|
|
private:
|
|
friend class clang::SarifDocumentWriter;
|
|
|
|
std::optional<uint32_t> Offset;
|
|
std::optional<size_t> Length;
|
|
std::string MimeType;
|
|
SarifArtifactLocation Location;
|
|
llvm::SmallVector<std::string, 4> Roles;
|
|
|
|
SarifArtifact() = delete;
|
|
|
|
explicit SarifArtifact(const SarifArtifactLocation &Loc) : Location(Loc) {}
|
|
|
|
public:
|
|
static SarifArtifact create(const SarifArtifactLocation &Loc) {
|
|
return SarifArtifact{Loc};
|
|
}
|
|
|
|
SarifArtifact setOffset(uint32_t ArtifactOffset) {
|
|
Offset = ArtifactOffset;
|
|
return *this;
|
|
}
|
|
|
|
SarifArtifact setLength(size_t NumBytes) {
|
|
Length = NumBytes;
|
|
return *this;
|
|
}
|
|
|
|
SarifArtifact setRoles(std::initializer_list<llvm::StringRef> ArtifactRoles) {
|
|
Roles.assign(ArtifactRoles.begin(), ArtifactRoles.end());
|
|
return *this;
|
|
}
|
|
|
|
SarifArtifact setMimeType(llvm::StringRef ArtifactMimeType) {
|
|
MimeType = ArtifactMimeType.str();
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
enum class ThreadFlowImportance { Important, Essential, Unimportant };
|
|
|
|
/// The level of severity associated with a \ref SarifResult.
|
|
///
|
|
/// Of all the levels, \c None is the only one that is not associated with
|
|
/// a failure.
|
|
///
|
|
/// A typical mapping for clang's DiagnosticKind to SarifResultLevel would look
|
|
/// like:
|
|
/// * \c None: \ref clang::DiagnosticsEngine::Level::Remark, \ref clang::DiagnosticsEngine::Level::Ignored
|
|
/// * \c Note: \ref clang::DiagnosticsEngine::Level::Note
|
|
/// * \c Warning: \ref clang::DiagnosticsEngine::Level::Warning
|
|
/// * \c Error could be generated from one of:
|
|
/// - \ref clang::DiagnosticsEngine::Level::Warning with \c -Werror
|
|
/// - \ref clang::DiagnosticsEngine::Level::Error
|
|
/// - \ref clang::DiagnosticsEngine::Level::Fatal when \ref clang::DiagnosticsEngine::ErrorsAsFatal is set.
|
|
///
|
|
/// Reference:
|
|
/// 1. <a href="https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317648">level property</a>
|
|
enum class SarifResultLevel { None, Note, Warning, Error };
|
|
|
|
/// A thread flow is a sequence of code locations that specify a possible path
|
|
/// through a single thread of execution.
|
|
/// A thread flow in SARIF is related to a code flow which describes
|
|
/// the progress of one or more programs through one or more thread flows.
|
|
///
|
|
/// Reference:
|
|
/// 1. <a href="https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317744">threadFlow object</a>
|
|
/// 2. <a href="https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317740">codeFlow object</a>
|
|
class ThreadFlow {
|
|
friend class SarifDocumentWriter;
|
|
|
|
CharSourceRange Range;
|
|
ThreadFlowImportance Importance;
|
|
std::string Message;
|
|
|
|
ThreadFlow() = default;
|
|
|
|
public:
|
|
static ThreadFlow create() { return {}; }
|
|
|
|
ThreadFlow setRange(const CharSourceRange &ItemRange) {
|
|
assert(ItemRange.isCharRange() &&
|
|
"ThreadFlows require a character granular source range!");
|
|
Range = ItemRange;
|
|
return *this;
|
|
}
|
|
|
|
ThreadFlow setImportance(const ThreadFlowImportance &ItemImportance) {
|
|
Importance = ItemImportance;
|
|
return *this;
|
|
}
|
|
|
|
ThreadFlow setMessage(llvm::StringRef ItemMessage) {
|
|
Message = ItemMessage.str();
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
/// A SARIF Reporting Configuration (\c reportingConfiguration) object contains
|
|
/// properties for a \ref SarifRule that can be configured at runtime before
|
|
/// analysis begins.
|
|
///
|
|
/// Reference:
|
|
/// 1. <a href="https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317852">reportingConfiguration object</a>
|
|
class SarifReportingConfiguration {
|
|
friend class clang::SarifDocumentWriter;
|
|
|
|
bool Enabled = true;
|
|
SarifResultLevel Level = SarifResultLevel::Warning;
|
|
float Rank = -1.0f;
|
|
|
|
SarifReportingConfiguration() = default;
|
|
|
|
public:
|
|
static SarifReportingConfiguration create() { return {}; };
|
|
|
|
SarifReportingConfiguration disable() {
|
|
Enabled = false;
|
|
return *this;
|
|
}
|
|
|
|
SarifReportingConfiguration enable() {
|
|
Enabled = true;
|
|
return *this;
|
|
}
|
|
|
|
SarifReportingConfiguration setLevel(SarifResultLevel TheLevel) {
|
|
Level = TheLevel;
|
|
return *this;
|
|
}
|
|
|
|
SarifReportingConfiguration setRank(float TheRank) {
|
|
assert(TheRank >= 0.0f && "Rule rank cannot be smaller than 0.0");
|
|
assert(TheRank <= 100.0f && "Rule rank cannot be larger than 100.0");
|
|
Rank = TheRank;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
/// A SARIF rule (\c reportingDescriptor object) contains information that
|
|
/// describes a reporting item generated by a tool. A reporting item is
|
|
/// either a result of analysis or notification of a condition encountered by
|
|
/// the tool. Rules are arbitrary but are identifiable by a hierarchical
|
|
/// rule-id.
|
|
///
|
|
/// This builder provides an interface to create SARIF \c reportingDescriptor
|
|
/// objects via the \ref SarifRule::create static method.
|
|
///
|
|
/// Reference:
|
|
/// 1. <a href="https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317836">reportingDescriptor object</a>
|
|
class SarifRule {
|
|
friend class clang::SarifDocumentWriter;
|
|
|
|
std::string Name;
|
|
std::string Id;
|
|
std::string Description;
|
|
std::string HelpURI;
|
|
SarifReportingConfiguration DefaultConfiguration;
|
|
|
|
SarifRule() : DefaultConfiguration(SarifReportingConfiguration::create()) {}
|
|
|
|
public:
|
|
static SarifRule create() { return {}; }
|
|
|
|
SarifRule setName(llvm::StringRef RuleName) {
|
|
Name = RuleName.str();
|
|
return *this;
|
|
}
|
|
|
|
SarifRule setRuleId(llvm::StringRef RuleId) {
|
|
Id = RuleId.str();
|
|
return *this;
|
|
}
|
|
|
|
SarifRule setDescription(llvm::StringRef RuleDesc) {
|
|
Description = RuleDesc.str();
|
|
return *this;
|
|
}
|
|
|
|
SarifRule setHelpURI(llvm::StringRef RuleHelpURI) {
|
|
HelpURI = RuleHelpURI.str();
|
|
return *this;
|
|
}
|
|
|
|
SarifRule
|
|
setDefaultConfiguration(const SarifReportingConfiguration &Configuration) {
|
|
DefaultConfiguration = Configuration;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
/// A SARIF result (also called a "reporting item") is a unit of output
|
|
/// produced when one of the tool's \c reportingDescriptor encounters a match
|
|
/// on the file being analysed by the tool.
|
|
///
|
|
/// This builder provides a \ref SarifResult::create static method that can be
|
|
/// used to create an empty shell onto which attributes can be added using the
|
|
/// \c setX(...) methods.
|
|
///
|
|
/// For example:
|
|
/// \code{.cpp}
|
|
/// SarifResult result = SarifResult::create(...)
|
|
/// .setRuleId(...)
|
|
/// .setDiagnosticMessage(...);
|
|
/// \endcode
|
|
///
|
|
/// Reference:
|
|
/// 1. <a href="https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317638">SARIF<pre>result</pre></a>
|
|
class SarifResult {
|
|
friend class clang::SarifDocumentWriter;
|
|
|
|
// NOTE:
|
|
// This type cannot fit all possible indexes representable by JSON, but is
|
|
// chosen because it is the largest unsigned type that can be safely
|
|
// converted to an \c int64_t.
|
|
uint32_t RuleIdx;
|
|
std::string RuleId;
|
|
std::string DiagnosticMessage;
|
|
llvm::SmallVector<CharSourceRange, 8> Locations;
|
|
llvm::SmallVector<ThreadFlow, 8> ThreadFlows;
|
|
std::optional<SarifResultLevel> LevelOverride;
|
|
|
|
SarifResult() = delete;
|
|
explicit SarifResult(uint32_t RuleIdx) : RuleIdx(RuleIdx) {}
|
|
|
|
public:
|
|
static SarifResult create(uint32_t RuleIdx) { return SarifResult{RuleIdx}; }
|
|
|
|
SarifResult setIndex(uint32_t Idx) {
|
|
RuleIdx = Idx;
|
|
return *this;
|
|
}
|
|
|
|
SarifResult setRuleId(llvm::StringRef Id) {
|
|
RuleId = Id.str();
|
|
return *this;
|
|
}
|
|
|
|
SarifResult setDiagnosticMessage(llvm::StringRef Message) {
|
|
DiagnosticMessage = Message.str();
|
|
return *this;
|
|
}
|
|
|
|
SarifResult setLocations(llvm::ArrayRef<CharSourceRange> DiagLocs) {
|
|
#ifndef NDEBUG
|
|
for (const auto &Loc : DiagLocs) {
|
|
assert(Loc.isCharRange() &&
|
|
"SARIF Results require character granular source ranges!");
|
|
}
|
|
#endif
|
|
Locations.assign(DiagLocs.begin(), DiagLocs.end());
|
|
return *this;
|
|
}
|
|
SarifResult setThreadFlows(llvm::ArrayRef<ThreadFlow> ThreadFlowResults) {
|
|
ThreadFlows.assign(ThreadFlowResults.begin(), ThreadFlowResults.end());
|
|
return *this;
|
|
}
|
|
|
|
SarifResult setDiagnosticLevel(const SarifResultLevel &TheLevel) {
|
|
LevelOverride = TheLevel;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
/// This class handles creating a valid SARIF document given various input
|
|
/// attributes. However, it requires an ordering among certain method calls:
|
|
///
|
|
/// 1. Because every SARIF document must contain at least 1 \c run, callers
|
|
/// must ensure that \ref SarifDocumentWriter::createRun is called before
|
|
/// any other methods.
|
|
/// 2. If SarifDocumentWriter::endRun is called, callers MUST call
|
|
/// SarifDocumentWriter::createRun, before invoking any of the result
|
|
/// aggregation methods such as SarifDocumentWriter::appendResult etc.
|
|
class SarifDocumentWriter {
|
|
private:
|
|
const llvm::StringRef SchemaURI{
|
|
"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/"
|
|
"sarif-schema-2.1.0.json"};
|
|
const llvm::StringRef SchemaVersion{"2.1.0"};
|
|
|
|
/// \internal
|
|
/// Return a pointer to the current tool. Asserts that a run exists.
|
|
llvm::json::Object &getCurrentTool();
|
|
|
|
/// \internal
|
|
/// Checks if there is a run associated with this document.
|
|
///
|
|
/// \return true on success
|
|
bool hasRun() const;
|
|
|
|
/// \internal
|
|
/// Reset portions of the internal state so that the document is ready to
|
|
/// receive data for a new run.
|
|
void reset();
|
|
|
|
/// \internal
|
|
/// Return a mutable reference to the current run, after asserting it exists.
|
|
///
|
|
/// \note It is undefined behavior to call this if a run does not exist in
|
|
/// the SARIF document.
|
|
llvm::json::Object &getCurrentRun();
|
|
|
|
/// Create a code flow object for the given threadflows.
|
|
/// See \ref ThreadFlow.
|
|
///
|
|
/// \note It is undefined behavior to call this if a run does not exist in
|
|
/// the SARIF document.
|
|
llvm::json::Object
|
|
createCodeFlow(const llvm::ArrayRef<ThreadFlow> ThreadFlows);
|
|
|
|
/// Add the given threadflows to the ones this SARIF document knows about.
|
|
llvm::json::Array
|
|
createThreadFlows(const llvm::ArrayRef<ThreadFlow> ThreadFlows);
|
|
|
|
/// Add the given \ref CharSourceRange to the SARIF document as a physical
|
|
/// location, with its corresponding artifact.
|
|
llvm::json::Object createPhysicalLocation(const CharSourceRange &R);
|
|
|
|
public:
|
|
SarifDocumentWriter() = delete;
|
|
|
|
/// Create a new empty SARIF document with the given source manager.
|
|
SarifDocumentWriter(const SourceManager &SourceMgr) : SourceMgr(SourceMgr) {}
|
|
|
|
/// Release resources held by this SARIF document.
|
|
~SarifDocumentWriter() = default;
|
|
|
|
/// Create a new run with which any upcoming analysis will be associated.
|
|
/// Each run requires specifying the tool that is generating reporting items.
|
|
void createRun(const llvm::StringRef ShortToolName,
|
|
const llvm::StringRef LongToolName,
|
|
const llvm::StringRef ToolVersion = CLANG_VERSION_STRING);
|
|
|
|
/// If there is a current run, end it.
|
|
///
|
|
/// This method collects various book-keeping required to clear and close
|
|
/// resources associated with the current run, but may also allocate some
|
|
/// for the next run.
|
|
///
|
|
/// Calling \ref endRun before associating a run through \ref createRun leads
|
|
/// to undefined behaviour.
|
|
void endRun();
|
|
|
|
/// Associate the given rule with the current run.
|
|
///
|
|
/// Returns an integer rule index for the created rule that is unique within
|
|
/// the current run, which can then be used to create a \ref SarifResult
|
|
/// to add to the current run. Note that a rule must exist before being
|
|
/// referenced by a result.
|
|
///
|
|
/// \pre
|
|
/// There must be a run associated with the document, failing to do so will
|
|
/// cause undefined behaviour.
|
|
size_t createRule(const SarifRule &Rule);
|
|
|
|
/// Append a new result to the currently in-flight run.
|
|
///
|
|
/// \pre
|
|
/// There must be a run associated with the document, failing to do so will
|
|
/// cause undefined behaviour.
|
|
/// \pre
|
|
/// \c RuleIdx used to create the result must correspond to a rule known by
|
|
/// the SARIF document. It must be the value returned by a previous call
|
|
/// to \ref createRule.
|
|
void appendResult(const SarifResult &SarifResult);
|
|
|
|
/// Return the SARIF document in its current state.
|
|
/// Calling this will trigger a copy of the internal state including all
|
|
/// reported diagnostics, resulting in an expensive call.
|
|
llvm::json::Object createDocument();
|
|
|
|
private:
|
|
/// Source Manager to use for the current SARIF document.
|
|
const SourceManager &SourceMgr;
|
|
|
|
/// Flag to track the state of this document:
|
|
/// A closed document is one on which a new runs must be created.
|
|
/// This could be a document that is freshly created, or has recently
|
|
/// finished writing to a previous run.
|
|
bool Closed = true;
|
|
|
|
/// A sequence of SARIF runs.
|
|
/// Each run object describes a single run of an analysis tool and contains
|
|
/// the output of that run.
|
|
///
|
|
/// Reference: <a href="https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317484">run object</a>
|
|
llvm::json::Array Runs;
|
|
|
|
/// The list of rules associated with the most recent active run. These are
|
|
/// defined using the diagnostics passed to the SarifDocument. Each rule
|
|
/// need not be unique through the result set. E.g. there may be several
|
|
/// 'syntax' errors throughout code under analysis, each of which has its
|
|
/// own specific diagnostic message (and consequently, RuleId). Rules are
|
|
/// also known as "reportingDescriptor" objects in SARIF.
|
|
///
|
|
/// Reference: <a href="https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317556">rules property</a>
|
|
llvm::SmallVector<SarifRule, 32> CurrentRules;
|
|
|
|
/// The list of artifacts that have been encountered on the most recent active
|
|
/// run. An artifact is defined in SARIF as a sequence of bytes addressable
|
|
/// by a URI. A common example for clang's case would be files named by
|
|
/// filesystem paths.
|
|
llvm::StringMap<detail::SarifArtifact> CurrentArtifacts;
|
|
};
|
|
} // namespace clang
|
|
|
|
#endif // LLVM_CLANG_BASIC_SARIF_H
|