Viewing file: Sarif.h (17.8 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
//== 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
|