aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/include/clang/Basic/Sarif.h
blob: e6c46224b316d572c2526fdc0ea1b51b0411c122 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
//== 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