[clang] [clang-tools-extra] [clang-tidy] Add SARIF output to clang-tidy (PR #219182)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 1 02:09:08 PDT 2026
https://github.com/matthew-j-code updated https://github.com/llvm/llvm-project/pull/219182
>From 66c0ac42fb7de98a6f244116830917eb22bd875a Mon Sep 17 00:00:00 2001
From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)"
<jones.matthew at siemens.com>
Date: Fri, 10 Jul 2026 07:11:06 -0700
Subject: [PATCH 01/12] [clang-tidy] Add SARIF output to clang-tidy
Clang-tidy is a static analysis framework, and
should therefore be able to output results in the
Static Analysis Results Interchange Format
(SARIF). This brings clang-tidy in line with clang
and clang-static-analyzer.
---
clang-tools-extra/clang-tidy/ClangTidy.cpp | 130 +++++++++++++-
clang-tools-extra/clang-tidy/ClangTidy.h | 3 +-
.../clang-tidy/tool/ClangTidyMain.cpp | 21 ++-
clang-tools-extra/docs/ReleaseNotes.md | 4 +
.../export-SARIF-Thread-flows.cpp | 136 +++++++++++++++
.../export-SARIF-diagnostics-terminal.cpp | 98 +++++++++++
.../export-SARIF-diagnostics.cpp | 165 ++++++++++++++++++
.../include/clang/Frontend/SARIFDiagnostic.h | 16 +-
clang/lib/Frontend/SARIFDiagnostic.cpp | 60 ++++---
9 files changed, 593 insertions(+), 40 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-terminal.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics.cpp
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index d065bb2c4bcfc..05084b33a92b3 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -24,10 +24,12 @@
#include "clang/AST/ASTConsumer.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/DiagnosticFrontend.h"
+#include "clang/Basic/Sarif.h"
#include "clang/Format/Format.h"
#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/MultiplexConsumer.h"
+#include "clang/Frontend/SARIFDiagnostic.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h"
@@ -36,6 +38,7 @@
#include "clang/Tooling/DiagnosticsYaml.h" // IWYU pragma: keep
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/Process.h"
#include <memory>
#include <utility>
@@ -101,11 +104,18 @@ class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {
class ErrorReporter {
public:
ErrorReporter(ClangTidyContext &Context, FixBehaviour ApplyFixes,
- llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS)
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
+ llvm::raw_ostream *SarifOS = nullptr)
: Files(FileSystemOptions(), std::move(BaseFS)),
DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), DiagOpts)),
Diags(DiagnosticIDs::create(), DiagOpts, DiagPrinter),
- SourceMgr(Diags, Files), Context(Context), ApplyFixes(ApplyFixes) {
+ SourceMgr(Diags, Files), Context(Context), ApplyFixes(ApplyFixes),
+ SarifOS(SarifOS) {
+ if (SarifOS) {
+ SarifWriter.emplace(SourceMgr);
+ SarifWriter->createRun("clang-tidy", "clang-tidy");
+ }
+
DiagOpts.setShowColors(Context.getOptions().UseColor.value_or(
llvm::sys::Process::StandardOutHasColors())
? ShowColorsKind::On
@@ -195,6 +205,97 @@ class ErrorReporter {
}
for (const auto &Note : Error.Notes)
reportNote(Note);
+
+ if (SarifWriter)
+ exportSarifResult(Error, Loc);
+ }
+
+ SmallVector<ThreadFlow, 8> createThreadFlows(const ClangTidyError &Error) {
+ SmallVector<ThreadFlow, 8> Flows;
+ if (!StringRef(Error.DiagnosticName).starts_with("clang-analyzer-"))
+ return Flows;
+
+ Flows.reserve(Error.Notes.size());
+ for (const tooling::DiagnosticMessage &Note : Error.Notes) {
+ tooling::FileByteRange FBR;
+ if (Note.Ranges.empty()) {
+ FBR.FilePath = Note.FilePath;
+ FBR.FileOffset = Note.FileOffset;
+ FBR.Length = 1;
+ } else {
+ FBR = Note.Ranges.front();
+ }
+ const CharSourceRange Range = getRange(FBR);
+ Flows.push_back(
+ ThreadFlow::create().setRange(Range).setMessage(Note.Message));
+ }
+ return Flows;
+ }
+
+ static std::string buildClangTidyHelpURI(StringRef Name) {
+ const auto [Module, Check] = Name.starts_with("clang-analyzer-")
+ ? Name.rsplit('-')
+ : Name.split('-');
+ return llvm::formatv(
+ "https://clang.llvm.org/extra/clang-tidy/checks/{0}/{1}.html",
+ Module, Check);
+ }
+
+ void exportSarifResult(const ClangTidyError &Error,
+ const SourceLocation Loc) {
+ assert(SarifWriter &&
+ "SarifWriter must be initialized to export SARIF results");
+
+ const std::pair<llvm::StringMap<size_t>::iterator, bool> RuleIndexEntry =
+ SarifRuleIdx.try_emplace(Error.DiagnosticName, 0);
+ llvm::StringMap<size_t>::iterator RuleIndexEntryIt = RuleIndexEntry.first;
+ bool Inserted = RuleIndexEntry.second;
+ size_t &RuleIndex = RuleIndexEntryIt->second;
+
+ const DiagnosticsEngine::Level EffectiveLevel =
+ Error.IsWarningAsError
+ ? DiagnosticsEngine::Error
+ : static_cast<DiagnosticsEngine::Level>(Error.DiagLevel);
+
+ if (Inserted) {
+ const StringRef Name = Error.DiagnosticName;
+ SarifRule Rule =
+ SarifRule::create().setRuleId(Name).setName(Name).setDescription(
+ Error.Message.Message);
+
+ if (!Name.starts_with("clang-diagnostic"))
+ Rule = Rule.setHelpURI(buildClangTidyHelpURI(Name));
+
+ Rule = addDiagnosticLevelToRule(Rule, EffectiveLevel);
+ RuleIndex = SarifWriter->createRule(Rule);
+ }
+
+ SarifResult Result =
+ SarifResult::create(RuleIndex)
+ .setDiagnosticMessage(Error.Message.Message)
+ .setDiagnosticLevel(getSarifResultLevel(EffectiveLevel))
+ .setThreadFlows(createThreadFlows(Error))
+ .addLocations(getResultRanges(Error, Loc));
+
+ SarifWriter->appendResult(Result);
+ }
+
+ SmallVector<CharSourceRange, 4> getResultRanges(const ClangTidyError &Error,
+ SourceLocation Loc) {
+ SmallVector<CharSourceRange, 4> Ranges;
+ Ranges.reserve(Error.Message.Ranges.size());
+ for (const FileByteRange &FBR : Error.Message.Ranges)
+ Ranges.push_back(getRange(FBR));
+
+ if (Ranges.empty() && Loc.isValid()) {
+ // Some Clang-Tidy diagnostics are issued with a single location (not a
+ // range). For these, we create a range of length 1 at the diagnostic
+ // location. As, SARIF results require a character range for each
+ // location.
+ Ranges.push_back(
+ CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(1)));
+ }
+ return Ranges;
}
void finish() {
@@ -254,6 +355,8 @@ class ErrorReporter {
if (OriginalCWD)
VFS.setCurrentWorkingDirectory(*OriginalCWD);
}
+ if (SarifWriter)
+ finalizeSarif();
}
unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors; }
@@ -271,6 +374,14 @@ class ErrorReporter {
return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
}
+ void finalizeSarif() {
+ assert(SarifWriter &&
+ "SarifWriter must be initialized to export SARIF results");
+ assert(SarifOS && "SarifOS must be initialized to export SARIF results");
+ llvm::json::Value Document = SarifWriter->createDocument();
+ *SarifOS << llvm::formatv("{0:2}", Document);
+ }
+
void reportFix(const DiagnosticBuilder &Diag,
const llvm::StringMap<Replacements> &Fix) {
for (const auto &FileAndReplacements : Fix) {
@@ -328,6 +439,13 @@ class ErrorReporter {
unsigned TotalFixes = 0U;
unsigned AppliedFixes = 0U;
unsigned WarningsAsErrors = 0U;
+ llvm::raw_ostream *SarifOS = nullptr;
+ // ExportSarifResult is called for each SARIF rule that gets created, while a
+ // SarifWriter run is created once at ErrorReporter construction.
+ std::optional<SarifDocumentWriter> SarifWriter;
+ // SarifRuleIdx is used across multiple calls of exportSarifResult as cache to
+ // avoid creating duplicate SARIF rules.
+ llvm::StringMap<size_t> SarifRuleIdx;
};
class ClangTidyASTConsumer : public MultiplexConsumer {
@@ -613,7 +731,8 @@ runClangTidy(ClangTidyContext &Context, const CompilationDatabase &Compilations,
Context.setEnableProfiling(EnableCheckProfile);
Context.setProfileStoragePrefix(StoreCheckProfile);
- ClangTidyDiagnosticConsumer DiagConsumer(Context, nullptr, true, ApplyAnyFix);
+ ClangTidyDiagnosticConsumer DiagConsumer(Context, nullptr, true, ApplyAnyFix,
+ true);
auto DiagOpts = std::make_unique<DiagnosticOptions>();
DiagnosticsEngine DE(DiagnosticIDs::create(), *DiagOpts, &DiagConsumer,
/*ShouldOwnClient=*/false);
@@ -668,8 +787,9 @@ runClangTidy(ClangTidyContext &Context, const CompilationDatabase &Compilations,
void handleErrors(llvm::ArrayRef<ClangTidyError> Errors,
ClangTidyContext &Context, FixBehaviour Fix,
unsigned &WarningsAsErrorsCount,
- llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS) {
- ErrorReporter Reporter(Context, Fix, std::move(BaseFS));
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
+ llvm::raw_ostream *SarifOS) {
+ ErrorReporter Reporter(Context, Fix, std::move(BaseFS), SarifOS);
llvm::vfs::FileSystem &FileSystem =
Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.h b/clang-tools-extra/clang-tidy/ClangTidy.h
index 5fac25bd3bbca..5069cb3cca629 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.h
+++ b/clang-tools-extra/clang-tidy/ClangTidy.h
@@ -118,7 +118,8 @@ enum FixBehaviour {
void handleErrors(llvm::ArrayRef<ClangTidyError> Errors,
ClangTidyContext &Context, FixBehaviour Fix,
unsigned &WarningsAsErrorsCount,
- llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS);
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
+ llvm::raw_ostream *SarifOS = nullptr);
/// Serializes replacements into YAML and writes them to the specified
/// output stream.
diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
index 7ee556fa7c51f..cc39ae2b507bf 100644
--- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
+++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
@@ -21,6 +21,7 @@
#include "clang/Tooling/CommonOptionsParser.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/PluginLoader.h" // IWYU pragma: keep
#include "llvm/Support/Process.h"
@@ -311,6 +312,12 @@ code with clang-apply-replacements.
cl::value_desc("filename"),
cl::cat(ClangTidyCategory));
+static cl::opt<std::string> ExportSarif("export-sarif", desc(R"(
+File in which to store diagnostics in SARIF format.
+)"),
+ cl::value_desc("filename"),
+ cl::cat(ClangTidyCategory));
+
static cl::opt<bool> Quiet("quiet", desc(R"(
Run clang-tidy in quiet mode. This suppresses
printing statistics about ignored warnings and
@@ -652,6 +659,18 @@ int clangTidyMain(int argc, const char **argv) {
FileName = PathList.front();
const SmallString<256> FilePath = makeAbsolute(FileName);
+
+ std::unique_ptr<llvm::raw_fd_ostream> SarifOS;
+ if (!ExportSarif.empty()) {
+ std::error_code EC;
+ SarifOS = std::make_unique<llvm::raw_fd_ostream>(ExportSarif, EC,
+ llvm::sys::fs::OF_Text);
+ if (EC) {
+ llvm::errs() << "Error opening output file: " << EC.message() << '\n';
+ return 1;
+ }
+ }
+
ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FilePath);
const std::vector<std::string> EnabledChecks =
@@ -754,7 +773,7 @@ int clangTidyMain(int argc, const char **argv) {
unsigned WErrorCount = 0;
handleErrors(Errors, Context, DisableFixes ? FB_NoFix : Behaviour,
- WErrorCount, BaseFS);
+ WErrorCount, BaseFS, SarifOS.get());
if (!ExportFixes.empty() && !Errors.empty()) {
std::error_code EC;
diff --git a/clang-tools-extra/docs/ReleaseNotes.md b/clang-tools-extra/docs/ReleaseNotes.md
index 93fbe57a58321..4c9f10c4c7153 100644
--- a/clang-tools-extra/docs/ReleaseNotes.md
+++ b/clang-tools-extra/docs/ReleaseNotes.md
@@ -105,6 +105,10 @@ infrastructure are described first, followed by tool-specific sections.
piping from {program}`git` to {program}`clang-tidy-diff.py`, where slashes
will now be automatically normalized.
+- Added a new {program}`clang-tidy` command line option `-export-sarif=<file>`
+ in which clang-tidy diagnostics are exported in the `SARIF https://sarifweb.azurewebsites.net/`
+ format.
+
#### New checks
- New {doc}`llvm-invalid-regex-pattern
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
new file mode 100644
index 0000000000000..63ff3653cbc3f
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
@@ -0,0 +1,136 @@
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t-input.cpp
+// RUN: clang-tidy %t-input.cpp -checks='-*,clang-analyzer*' -export-sarif=%t.sarif > %t.msg 2>&1
+// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MESSAGES %s -implicit-check-not='{{warning|error|note}}:'
+// RUN: FileCheck -input-file=%t.sarif -check-prefix=CHECK-SARIF %s
+void f() {
+ int *ptr = nullptr;
+ *ptr = 1;
+}
+
+//CHECK-MESSAGES: -input.cpp:3:8: warning: Dereference of null pointer (loaded from variable 'ptr') [clang-analyzer-core.NullDereference]
+//CHECK-MESSAGES: -input.cpp:2:3: note: 'ptr' initialized to a null pointer value
+//CHECK-MESSAGES: -input.cpp:3:8: note: Dereference of null pointer (loaded from variable 'ptr')
+
+//CHECK-SARIF: {
+//CHECK-SARIF-NEXT: "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json",
+//CHECK-SARIF-NEXT: "runs": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "artifacts": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "length": {{[0-9]+}},
+//CHECK-SARIF-NEXT: "location": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "mimeType": "text/plain",
+//CHECK-SARIF-NEXT: "roles": [
+//CHECK-SARIF-NEXT: "resultFile"
+//CHECK-SARIF-NEXT: ]
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "columnKind": "unicodeCodePoints",
+//CHECK-SARIF-NEXT: "results": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "codeFlows": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "threadFlows": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "locations": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "importance": "important",
+//CHECK-SARIF-NEXT: "location": {
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "'ptr' initialized to a null pointer value"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 11,
+//CHECK-SARIF-NEXT: "endLine": 2,
+//CHECK-SARIF-NEXT: "startColumn": 3,
+//CHECK-SARIF-NEXT: "startLine": 2
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "importance": "important",
+//CHECK-SARIF-NEXT: "location": {
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "Dereference of null pointer (loaded from variable 'ptr')"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 7,
+//CHECK-SARIF-NEXT: "endLine": 3,
+//CHECK-SARIF-NEXT: "startColumn": 4,
+//CHECK-SARIF-NEXT: "startLine": 3
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ]
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ]
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "level": "warning",
+//CHECK-SARIF-NEXT: "locations": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 7,
+//CHECK-SARIF-NEXT: "endLine": 3,
+//CHECK-SARIF-NEXT: "startColumn": 4,
+//CHECK-SARIF-NEXT: "startLine": 3
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "Dereference of null pointer (loaded from variable 'ptr')"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "ruleId": "clang-analyzer-core.NullDereference",
+//CHECK-SARIF-NEXT: "ruleIndex": 0
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "tool": {
+//CHECK-SARIF-NEXT: "driver": {
+//CHECK-SARIF-NEXT: "fullName": "clang-tidy",
+//CHECK-SARIF-NEXT: "informationUri": "https://clang.llvm.org/docs/UsersManual.html",
+//CHECK-SARIF-NEXT: "language": "en-US",
+//CHECK-SARIF-NEXT: "name": "clang-tidy",
+//CHECK-SARIF-NEXT: "rules": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "defaultConfiguration": {
+//CHECK-SARIF-NEXT: "enabled": true,
+//CHECK-SARIF-NEXT: "level": "warning",
+//CHECK-SARIF-NEXT: "rank": -1
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "fullDescription": {
+//CHECK-SARIF-NEXT: "text": "Dereference of null pointer (loaded from variable 'ptr')"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "helpUri": "https://clang.llvm.org/extra/clang-tidy/checks/clang-analyzer/core.NullDereference.html",
+//CHECK-SARIF-NEXT: "id": "clang-analyzer-core.NullDereference",
+//CHECK-SARIF-NEXT: "name": "clang-analyzer-core.NullDereference"
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "version": "{{.*}}"
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "version": "{{.*}}"
+//CHECK-SARIF-NEXT: }
+
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-terminal.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-terminal.cpp
new file mode 100644
index 0000000000000..52fb3fc3a639c
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-terminal.cpp
@@ -0,0 +1,98 @@
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t-input.cpp
+// RUN: not clang-tidy %t-input.cpp -checks='-*,google-explicit-constructor,clang-diagnostic-missing-prototypes,clang-diagnostic-zero-length-array' --warnings-as-errors='clang-diagnostic-missing-prototypes,google-explicit-constructor' -export-sarif - -- -Wmissing-prototypes -Wzero-length-array 2>/dev/null | FileCheck %s
+#define X(n) void n ## n() {}
+X(f)
+int a[-1];
+int b[0];
+
+void test(x);
+struct Foo {
+ member;
+ Foo(int) {}
+};
+
+//CHECK: {{^{$}}
+//CHECK-NEXT: "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json",
+//CHECK-NEXT: "runs": [
+//CHECK-NEXT: {
+//CHECK-NEXT: "artifacts": [
+//CHECK-NEXT: {
+//CHECK-NEXT: "length": {{[0-9]+}},
+//CHECK-NEXT: "location": {
+//CHECK-NEXT: "index": 0,
+//CHECK-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-NEXT: },
+//CHECK-NEXT: "mimeType": "text/plain",
+//CHECK-NEXT: "roles": [
+//CHECK-NEXT: "resultFile"
+//CHECK-NEXT: ]
+//CHECK-NEXT: }
+//CHECK-NEXT: ],
+//CHECK-NEXT: "columnKind": "unicodeCodePoints",
+//CHECK-NEXT: "results": [
+//CHECK-NEXT: {
+//CHECK-NEXT: "level": "error",
+//CHECK-NEXT: "locations": [
+//CHECK-NEXT: {
+//CHECK-NEXT: "physicalLocation": {
+//CHECK-NEXT: "artifactLocation": {
+//CHECK-NEXT: "index": 0,
+//CHECK-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-NEXT: },
+//CHECK-NEXT: "region": {
+//CHECK-NEXT: "endColumn": 2,
+//CHECK-NEXT: "endLine": 2,
+//CHECK-NEXT: "startColumn": 1,
+//CHECK-NEXT: "startLine": 2
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: ],
+//CHECK-NEXT: "message": {
+//CHECK-NEXT: "text": "no previous prototype for function 'ff'"
+//CHECK-NEXT: },
+//CHECK-NEXT: "ruleId": "clang-diagnostic-missing-prototypes",
+//CHECK-NEXT: "ruleIndex": 0
+//CHECK-NEXT: },
+//CHECK-NEXT: {
+//CHECK-NEXT: "level": "error",
+//CHECK-NEXT: "locations": [
+//CHECK-NEXT: {
+//CHECK-NEXT: "physicalLocation": {
+//CHECK-NEXT: "artifactLocation": {
+//CHECK-NEXT: "index": 0,
+//CHECK-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-NEXT: },
+//CHECK-NEXT: "region": {
+//CHECK-NEXT: "endColumn": 9,
+//CHECK-NEXT: "endLine": 3,
+//CHECK-NEXT: "startColumn": 7,
+//CHECK-NEXT: "startLine": 3
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: ],
+//CHECK-NEXT: "message": {
+//CHECK-NEXT: "text": "'a' declared as an array with a negative size"
+//CHECK-NEXT: },
+//CHECK-NEXT: "ruleId": "clang-diagnostic-error",
+//CHECK-NEXT: "ruleIndex": 1
+//CHECK-NEXT: },
+//CHECK-NEXT: {
+//CHECK-NEXT: "level": "warning",
+//CHECK-NEXT: "locations": [
+//CHECK-NEXT: {
+//CHECK-NEXT: "physicalLocation": {
+//CHECK-NEXT: "artifactLocation": {
+//CHECK-NEXT: "index": 0,
+//CHECK-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-NEXT: },
+//CHECK-NEXT: "region": {
+//CHECK-NEXT: "endColumn": 8,
+//CHECK-NEXT: "endLine": 4,
+//CHECK-NEXT: "startColumn": 7,
+//CHECK-NEXT: "startLine": 4
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: ],
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics.cpp
new file mode 100644
index 0000000000000..4ba2d3d55a4ed
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics.cpp
@@ -0,0 +1,165 @@
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t-input.cpp
+// RUN: not clang-tidy %t-input.cpp -checks='-*,google-explicit-constructor,clang-diagnostic-missing-prototypes,clang-diagnostic-zero-length-array' --warnings-as-errors='clang-diagnostic-missing-prototypes,google-explicit-constructor' -export-sarif=%t.sarif -- -Wmissing-prototypes -Wzero-length-array > %t.msg 2>&1
+// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MESSAGES %s -implicit-check-not='{{warning|error|note}}:'
+// RUN: FileCheck -input-file=%t.sarif -check-prefix=CHECK-SARIF %s
+#define X(n) void n ## n() {}
+X(f)
+int a[-1];
+int b[0];
+
+void test(x);
+struct Foo {
+ member;
+ Foo(int) {}
+};
+
+//CHECK-MESSAGES: -input.cpp:2:1: error: no previous prototype for function 'ff' [clang-diagnostic-missing-prototypes,-warnings-as-errors]
+//CHECK-MESSAGES: -input.cpp:1:19: note: expanded from macro 'X'
+//CHECK-MESSAGES: {{^}}note: expanded from here{{$}}
+//CHECK-MESSAGES: -input.cpp:2:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
+//CHECK-MESSAGES: -input.cpp:1:14: note: expanded from macro 'X'
+//CHECK-MESSAGES: -input.cpp:3:7: error: 'a' declared as an array with a negative size [clang-diagnostic-error]
+//CHECK-MESSAGES: -input.cpp:4:7: warning: zero size arrays are an extension [clang-diagnostic-zero-length-array]
+//CHECK-MESSAGES: -input.cpp:6:11: error: unknown type name 'x' [clang-diagnostic-error]
+//CHECK-MESSAGES: -input.cpp:8:3: error: a type specifier is required for all declarations [clang-diagnostic-error]
+//CHECK-MESSAGES: -input.cpp:9:3: error: single-argument constructors must be marked explicit to avoid unintentional implicit conversions [google-explicit-constructor,-warnings-as-errors]
+
+//CHECK-SARIF: {
+//CHECK-SARIF-NEXT: "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json",
+//CHECK-SARIF-NEXT: "runs": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "artifacts": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "length": {{[0-9]+}},
+//CHECK-SARIF-NEXT: "location": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "mimeType": "text/plain",
+//CHECK-SARIF-NEXT: "roles": [
+//CHECK-SARIF-NEXT: "resultFile"
+//CHECK-SARIF-NEXT: ]
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "columnKind": "unicodeCodePoints",
+//CHECK-SARIF-NEXT: "results": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "level": "error",
+//CHECK-SARIF-NEXT: "locations": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 2,
+//CHECK-SARIF-NEXT: "endLine": 2,
+//CHECK-SARIF-NEXT: "startColumn": 1,
+//CHECK-SARIF-NEXT: "startLine": 2
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "no previous prototype for function 'ff'"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "ruleId": "clang-diagnostic-missing-prototypes",
+//CHECK-SARIF-NEXT: "ruleIndex": 0
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "level": "error",
+//CHECK-SARIF-NEXT: "locations": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 9,
+//CHECK-SARIF-NEXT: "endLine": 3,
+//CHECK-SARIF-NEXT: "startColumn": 7,
+//CHECK-SARIF-NEXT: "startLine": 3
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "'a' declared as an array with a negative size"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "ruleId": "clang-diagnostic-error",
+//CHECK-SARIF-NEXT: "ruleIndex": 1
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "level": "warning",
+//CHECK-SARIF-NEXT: "locations": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 8,
+//CHECK-SARIF-NEXT: "endLine": 4,
+//CHECK-SARIF-NEXT: "startColumn": 7,
+//CHECK-SARIF-NEXT: "startLine": 4
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "zero size arrays are an extension"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "ruleId": "clang-diagnostic-zero-length-array",
+//CHECK-SARIF-NEXT: "ruleIndex": 2
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "level": "error",
+//CHECK-SARIF-NEXT: "locations": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 12,
+//CHECK-SARIF-NEXT: "endLine": 6,
+//CHECK-SARIF-NEXT: "startColumn": 11,
+//CHECK-SARIF-NEXT: "startLine": 6
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "unknown type name 'x'"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "ruleId": "clang-diagnostic-error",
+//CHECK-SARIF-NEXT: "ruleIndex": 1
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "level": "error",
+//CHECK-SARIF-NEXT: "locations": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 4,
+//CHECK-SARIF-NEXT: "endLine": 8,
+//CHECK-SARIF-NEXT: "startColumn": 3,
+//CHECK-SARIF-NEXT: "startLine": 8
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "a type specifier is required for all declarations"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "ruleId": "clang-diagnostic-error",
+//CHECK-SARIF-NEXT: "ruleIndex": 1
+//CHECK-SARIF-NEXT: },
diff --git a/clang/include/clang/Frontend/SARIFDiagnostic.h b/clang/include/clang/Frontend/SARIFDiagnostic.h
index 7a6f27eb3b9fa..cef565db1686a 100644
--- a/clang/include/clang/Frontend/SARIFDiagnostic.h
+++ b/clang/include/clang/Frontend/SARIFDiagnostic.h
@@ -6,8 +6,9 @@
//
//===----------------------------------------------------------------------===//
//
-// This is a utility class that provides support for constructing a SARIF object
-// containing diagnostics.
+// This file implements SARIF diagnostic emission, including the SARIFDiagnostic
+// renderer and helper utilities for mapping diagnostic levels and annotating
+// SARIF rules.
//
//===----------------------------------------------------------------------===//
@@ -20,6 +21,14 @@
namespace clang {
+// Maps clang DiagnosticsEngine level to SARIF result levels.
+SarifResultLevel getSarifResultLevel(DiagnosticsEngine::Level level);
+
+// Creates configuration for a SARIF rule based on the diagnostic level and sets
+// the rank.
+SarifRule addDiagnosticLevelToRule(SarifRule Rule,
+ DiagnosticsEngine::Level Level);
+
class SARIFDiagnostic : public DiagnosticRenderer {
public:
SARIFDiagnostic(raw_ostream &OS, const LangOptions &LangOpts,
@@ -70,9 +79,6 @@ class SARIFDiagnostic : public DiagnosticRenderer {
getSarifLocation(FullSourceLoc Loc, PresumedLoc PLoc,
ArrayRef<CharSourceRange> Ranges);
- SarifRule addDiagnosticLevelToRule(SarifRule Rule,
- DiagnosticsEngine::Level Level);
-
llvm::StringRef emitFilename(StringRef Filename, const SourceManager &SM);
llvm::SmallVector<std::pair<FullSourceLoc, PresumedLoc>>
diff --git a/clang/lib/Frontend/SARIFDiagnostic.cpp b/clang/lib/Frontend/SARIFDiagnostic.cpp
index 5183462b76f41..b2df1772e4ee5 100644
--- a/clang/lib/Frontend/SARIFDiagnostic.cpp
+++ b/clang/lib/Frontend/SARIFDiagnostic.cpp
@@ -30,6 +30,38 @@
namespace clang {
+SarifResultLevel getSarifResultLevel(DiagnosticsEngine::Level level) {
+ switch (level) {
+ case DiagnosticsEngine::Ignored:
+ llvm_unreachable("Invalid diagnostic type");
+ case DiagnosticsEngine::Note:
+ return SarifResultLevel::Note;
+ case DiagnosticsEngine::Remark:
+ return SarifResultLevel::None;
+ case DiagnosticsEngine::Warning:
+ return SarifResultLevel::Warning;
+ case DiagnosticsEngine::Error:
+ case DiagnosticsEngine::Fatal:
+ return SarifResultLevel::Error;
+ }
+ llvm_unreachable("Potentially un-handled DiagnosticsEngine::Level. "
+ "Is the switch not fully covered?");
+}
+
+SarifRule addDiagnosticLevelToRule(SarifRule Rule,
+ DiagnosticsEngine::Level Level) {
+ SarifReportingConfiguration Config =
+ SarifReportingConfiguration::create().setLevel(
+ getSarifResultLevel(Level));
+
+ if (Level == DiagnosticsEngine::Error)
+ Config = Config.setRank(50);
+ else if (Level == DiagnosticsEngine::Fatal)
+ Config = Config.setRank(100);
+
+ return Rule.setDefaultConfiguration(Config);
+}
+
SARIFDiagnostic::SARIFDiagnostic(raw_ostream &OS, const LangOptions &LangOpts,
DiagnosticOptions &DiagOpts,
SarifDocumentWriter *Writer)
@@ -164,34 +196,6 @@ SARIFDiagnostic::getSarifLocation(FullSourceLoc Loc, PresumedLoc PLoc,
return Locations;
}
-SarifRule
-SARIFDiagnostic::addDiagnosticLevelToRule(SarifRule Rule,
- DiagnosticsEngine::Level Level) {
- auto Config = SarifReportingConfiguration::create();
-
- switch (Level) {
- case DiagnosticsEngine::Note:
- Config = Config.setLevel(SarifResultLevel::Note);
- break;
- case DiagnosticsEngine::Remark:
- Config = Config.setLevel(SarifResultLevel::None);
- break;
- case DiagnosticsEngine::Warning:
- Config = Config.setLevel(SarifResultLevel::Warning);
- break;
- case DiagnosticsEngine::Error:
- Config = Config.setLevel(SarifResultLevel::Error).setRank(50);
- break;
- case DiagnosticsEngine::Fatal:
- Config = Config.setLevel(SarifResultLevel::Error).setRank(100);
- break;
- case DiagnosticsEngine::Ignored:
- assert(false && "Invalid diagnostic type");
- }
-
- return Rule.setDefaultConfiguration(Config);
-}
-
llvm::StringRef SARIFDiagnostic::emitFilename(StringRef Filename,
const SourceManager &SM) {
if (DiagOpts.AbsolutePath) {
>From 40cefa8d6a8cc71d3a9c702342ddd114404a7d55 Mon Sep 17 00:00:00 2001
From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)"
<jones.matthew at siemens.com>
Date: Fri, 28 Aug 2026 04:09:07 -0700
Subject: [PATCH 02/12] Fixed unchecked optional access and const-correctness
---
clang-tools-extra/clang-tidy/ClangTidy.cpp | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index 05084b33a92b3..3fce3d42bec8a 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -237,8 +237,8 @@ class ErrorReporter {
? Name.rsplit('-')
: Name.split('-');
return llvm::formatv(
- "https://clang.llvm.org/extra/clang-tidy/checks/{0}/{1}.html",
- Module, Check);
+ "https://clang.llvm.org/extra/clang-tidy/checks/{0}/{1}.html", Module,
+ Check);
}
void exportSarifResult(const ClangTidyError &Error,
@@ -248,8 +248,9 @@ class ErrorReporter {
const std::pair<llvm::StringMap<size_t>::iterator, bool> RuleIndexEntry =
SarifRuleIdx.try_emplace(Error.DiagnosticName, 0);
- llvm::StringMap<size_t>::iterator RuleIndexEntryIt = RuleIndexEntry.first;
- bool Inserted = RuleIndexEntry.second;
+ const llvm::StringMap<size_t>::iterator RuleIndexEntryIt =
+ RuleIndexEntry.first;
+ const bool Inserted = RuleIndexEntry.second;
size_t &RuleIndex = RuleIndexEntryIt->second;
const DiagnosticsEngine::Level EffectiveLevel =
@@ -267,17 +268,17 @@ class ErrorReporter {
Rule = Rule.setHelpURI(buildClangTidyHelpURI(Name));
Rule = addDiagnosticLevelToRule(Rule, EffectiveLevel);
- RuleIndex = SarifWriter->createRule(Rule);
+ RuleIndex = SarifWriter.value().createRule(Rule);
}
- SarifResult Result =
+ const SarifResult Result =
SarifResult::create(RuleIndex)
.setDiagnosticMessage(Error.Message.Message)
.setDiagnosticLevel(getSarifResultLevel(EffectiveLevel))
.setThreadFlows(createThreadFlows(Error))
.addLocations(getResultRanges(Error, Loc));
- SarifWriter->appendResult(Result);
+ SarifWriter.value().appendResult(Result);
}
SmallVector<CharSourceRange, 4> getResultRanges(const ClangTidyError &Error,
@@ -378,7 +379,7 @@ class ErrorReporter {
assert(SarifWriter &&
"SarifWriter must be initialized to export SARIF results");
assert(SarifOS && "SarifOS must be initialized to export SARIF results");
- llvm::json::Value Document = SarifWriter->createDocument();
+ llvm::json::Value Document = SarifWriter.value().createDocument();
*SarifOS << llvm::formatv("{0:2}", Document);
}
>From e4730344142a8ca6a97306b20b011a299e17e60b Mon Sep 17 00:00:00 2001
From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)"
<jones.matthew at siemens.com>
Date: Fri, 28 Aug 2026 06:08:54 -0700
Subject: [PATCH 03/12] Textdiagnosticprinter suppressed for stdout
---
clang-tools-extra/clang-tidy/ClangTidy.cpp | 36 ++++++++++---------
clang-tools-extra/clang-tidy/ClangTidy.h | 3 +-
.../clang-tidy/tool/ClangTidyMain.cpp | 3 +-
3 files changed, 24 insertions(+), 18 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index 3fce3d42bec8a..e8cca7831734f 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -105,9 +105,13 @@ class ErrorReporter {
public:
ErrorReporter(ClangTidyContext &Context, FixBehaviour ApplyFixes,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
- llvm::raw_ostream *SarifOS = nullptr)
+ llvm::raw_ostream *SarifOS = nullptr,
+ bool ExportSarifToStdout = false)
: Files(FileSystemOptions(), std::move(BaseFS)),
- DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), DiagOpts)),
+ DiagPrinter(
+ ExportSarifToStdout
+ ? static_cast<DiagnosticConsumer *>(new IgnoringDiagConsumer())
+ : new TextDiagnosticPrinter(llvm::outs(), DiagOpts)),
Diags(DiagnosticIDs::create(), DiagOpts, DiagPrinter),
SourceMgr(Diags, Files), Context(Context), ApplyFixes(ApplyFixes),
SarifOS(SarifOS) {
@@ -120,7 +124,8 @@ class ErrorReporter {
llvm::sys::Process::StandardOutHasColors())
? ShowColorsKind::On
: ShowColorsKind::Off);
- DiagPrinter->BeginSourceFile(LangOpts);
+ if (DiagPrinter)
+ DiagPrinter->BeginSourceFile(LangOpts);
if (DiagOpts.showColors(llvm::sys::Process::StandardOutHasColors()) &&
!llvm::sys::Process::StandardOutIsDisplayed())
llvm::sys::Process::UseANSIEscapeCodes(true);
@@ -246,11 +251,8 @@ class ErrorReporter {
assert(SarifWriter &&
"SarifWriter must be initialized to export SARIF results");
- const std::pair<llvm::StringMap<size_t>::iterator, bool> RuleIndexEntry =
+ const auto [RuleIndexEntryIt, Inserted] =
SarifRuleIdx.try_emplace(Error.DiagnosticName, 0);
- const llvm::StringMap<size_t>::iterator RuleIndexEntryIt =
- RuleIndexEntry.first;
- const bool Inserted = RuleIndexEntry.second;
size_t &RuleIndex = RuleIndexEntryIt->second;
const DiagnosticsEngine::Level EffectiveLevel =
@@ -268,7 +270,8 @@ class ErrorReporter {
Rule = Rule.setHelpURI(buildClangTidyHelpURI(Name));
Rule = addDiagnosticLevelToRule(Rule, EffectiveLevel);
- RuleIndex = SarifWriter.value().createRule(Rule);
+ if(SarifWriter)
+ RuleIndex = SarifWriter->createRule(Rule);
}
const SarifResult Result =
@@ -278,7 +281,8 @@ class ErrorReporter {
.setThreadFlows(createThreadFlows(Error))
.addLocations(getResultRanges(Error, Loc));
- SarifWriter.value().appendResult(Result);
+ if(SarifWriter)
+ SarifWriter->appendResult(Result);
}
SmallVector<CharSourceRange, 4> getResultRanges(const ClangTidyError &Error,
@@ -376,11 +380,10 @@ class ErrorReporter {
}
void finalizeSarif() {
- assert(SarifWriter &&
- "SarifWriter must be initialized to export SARIF results");
- assert(SarifOS && "SarifOS must be initialized to export SARIF results");
- llvm::json::Value Document = SarifWriter.value().createDocument();
- *SarifOS << llvm::formatv("{0:2}", Document);
+ if (SarifWriter && SarifOS) {
+ llvm::json::Value Document = SarifWriter->createDocument();
+ *SarifOS << llvm::formatv("{0:2}", Document);
+ }
}
void reportFix(const DiagnosticBuilder &Diag,
@@ -789,8 +792,9 @@ void handleErrors(llvm::ArrayRef<ClangTidyError> Errors,
ClangTidyContext &Context, FixBehaviour Fix,
unsigned &WarningsAsErrorsCount,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
- llvm::raw_ostream *SarifOS) {
- ErrorReporter Reporter(Context, Fix, std::move(BaseFS), SarifOS);
+ llvm::raw_ostream *SarifOS, bool ExportSarifToStdout) {
+ ErrorReporter Reporter(Context, Fix, std::move(BaseFS), SarifOS,
+ ExportSarifToStdout);
llvm::vfs::FileSystem &FileSystem =
Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.h b/clang-tools-extra/clang-tidy/ClangTidy.h
index 5069cb3cca629..6df3d7bcc2c3b 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.h
+++ b/clang-tools-extra/clang-tidy/ClangTidy.h
@@ -119,7 +119,8 @@ void handleErrors(llvm::ArrayRef<ClangTidyError> Errors,
ClangTidyContext &Context, FixBehaviour Fix,
unsigned &WarningsAsErrorsCount,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
- llvm::raw_ostream *SarifOS = nullptr);
+ llvm::raw_ostream *SarifOS = nullptr,
+ bool ExportSarifToStdout = false);
/// Serializes replacements into YAML and writes them to the specified
/// output stream.
diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
index cc39ae2b507bf..442f9bf3f9834 100644
--- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
+++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
@@ -773,7 +773,8 @@ int clangTidyMain(int argc, const char **argv) {
unsigned WErrorCount = 0;
handleErrors(Errors, Context, DisableFixes ? FB_NoFix : Behaviour,
- WErrorCount, BaseFS, SarifOS.get());
+ WErrorCount, BaseFS, SarifOS.get(),
+ SarifOS && ExportSarif == "-");
if (!ExportFixes.empty() && !Errors.empty()) {
std::error_code EC;
>From 48ebe8709f7dd89f38410653d77831d961619f1d Mon Sep 17 00:00:00 2001
From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)"
<jones.matthew at siemens.com>
Date: Fri, 28 Aug 2026 06:24:22 -0700
Subject: [PATCH 04/12] fixed formatting
---
clang-tools-extra/clang-tidy/ClangTidy.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index e8cca7831734f..a86db0fcd37e9 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -270,7 +270,7 @@ class ErrorReporter {
Rule = Rule.setHelpURI(buildClangTidyHelpURI(Name));
Rule = addDiagnosticLevelToRule(Rule, EffectiveLevel);
- if(SarifWriter)
+ if (SarifWriter)
RuleIndex = SarifWriter->createRule(Rule);
}
@@ -281,7 +281,7 @@ class ErrorReporter {
.setThreadFlows(createThreadFlows(Error))
.addLocations(getResultRanges(Error, Loc));
- if(SarifWriter)
+ if (SarifWriter)
SarifWriter->appendResult(Result);
}
>From 3a4e100820d275e642226c00345f4f7a6761ce9e Mon Sep 17 00:00:00 2001
From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)"
<jones.matthew at siemens.com>
Date: Tue, 1 Sep 2026 06:24:17 -0700
Subject: [PATCH 05/12] fixed invalid informationUri and removed incorrect
setDescription
---
clang-tools-extra/clang-tidy/ClangTidy.cpp | 3 +--
clang/lib/Basic/Sarif.cpp | 18 ++++++++++--------
2 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index a86db0fcd37e9..d7635346817b2 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -263,8 +263,7 @@ class ErrorReporter {
if (Inserted) {
const StringRef Name = Error.DiagnosticName;
SarifRule Rule =
- SarifRule::create().setRuleId(Name).setName(Name).setDescription(
- Error.Message.Message);
+ SarifRule::create().setRuleId(Name).setName(Name);
if (!Name.starts_with("clang-diagnostic"))
Rule = Rule.setHelpURI(buildClangTidyHelpURI(Name));
diff --git a/clang/lib/Basic/Sarif.cpp b/clang/lib/Basic/Sarif.cpp
index e829b9bb47ad9..5536e6a02091f 100644
--- a/clang/lib/Basic/Sarif.cpp
+++ b/clang/lib/Basic/Sarif.cpp
@@ -351,14 +351,16 @@ void SarifDocumentWriter::createRun(StringRef ShortToolName,
// Signify a new run has begun.
Closed = false;
- json::Object Tool{
- {"driver",
- json::Object{{"name", ShortToolName},
- {"fullName", LongToolName},
- {"language", "en-US"},
- {"version", ToolVersion},
- {"informationUri",
- "https://clang.llvm.org/docs/UsersManual.html"}}}};
+ StringRef InformationUri = "https://clang.llvm.org/docs/UsersManual.html";
+ if (LongToolName == "clang-tidy")
+ InformationUri = "https://clang.llvm.org/extra/clang-tidy/";
+
+ json::Object Tool{{"driver", json::Object{{"name", ShortToolName},
+ {"fullName", LongToolName},
+ {"language", "en-US"},
+ {"version", ToolVersion},
+ {"informationUri", InformationUri},
+ {"rules", json::Array{}}}}};
json::Object TheRun{{"tool", std::move(Tool)},
{"results", {}},
{"artifacts", {}},
>From cb3fa84148417525ee760ff9ec0fcd29f7fadb83 Mon Sep 17 00:00:00 2001
From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)"
<jones.matthew at siemens.com>
Date: Tue, 1 Sep 2026 08:00:57 -0700
Subject: [PATCH 06/12] fixed broken test
---
clang-tools-extra/clang-tidy/ClangTidy.cpp | 4 +---
.../clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp | 4 ++--
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index d7635346817b2..7fa5cff6ffcfd 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -262,9 +262,7 @@ class ErrorReporter {
if (Inserted) {
const StringRef Name = Error.DiagnosticName;
- SarifRule Rule =
- SarifRule::create().setRuleId(Name).setName(Name);
-
+ SarifRule Rule = SarifRule::create().setRuleId(Name).setName(Name);
if (!Name.starts_with("clang-diagnostic"))
Rule = Rule.setHelpURI(buildClangTidyHelpURI(Name));
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
index 63ff3653cbc3f..23f88e7c5bd18 100644
--- a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
@@ -108,7 +108,7 @@ void f() {
//CHECK-SARIF-NEXT: "tool": {
//CHECK-SARIF-NEXT: "driver": {
//CHECK-SARIF-NEXT: "fullName": "clang-tidy",
-//CHECK-SARIF-NEXT: "informationUri": "https://clang.llvm.org/docs/UsersManual.html",
+//CHECK-SARIF-NEXT: "informationUri": "https://clang.llvm.org/extra/clang-tidy/",
//CHECK-SARIF-NEXT: "language": "en-US",
//CHECK-SARIF-NEXT: "name": "clang-tidy",
//CHECK-SARIF-NEXT: "rules": [
@@ -119,7 +119,7 @@ void f() {
//CHECK-SARIF-NEXT: "rank": -1
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "fullDescription": {
-//CHECK-SARIF-NEXT: "text": "Dereference of null pointer (loaded from variable 'ptr')"
+//CHECK-SARIF-NEXT: "text": ""
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "helpUri": "https://clang.llvm.org/extra/clang-tidy/checks/clang-analyzer/core.NullDereference.html",
//CHECK-SARIF-NEXT: "id": "clang-analyzer-core.NullDereference",
>From 56ef347e28bed003d075d6b7b5d98c5d5ea1d5bb Mon Sep 17 00:00:00 2001
From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)"
<jones.matthew at siemens.com>
Date: Tue, 1 Sep 2026 08:35:23 -0700
Subject: [PATCH 07/12] Placeholder for desc
---
clang-tools-extra/clang-tidy/ClangTidy.cpp | 4 +++-
.../clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp | 2 +-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index 7fa5cff6ffcfd..29ca88680a7d9 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -262,7 +262,9 @@ class ErrorReporter {
if (Inserted) {
const StringRef Name = Error.DiagnosticName;
- SarifRule Rule = SarifRule::create().setRuleId(Name).setName(Name);
+ SarifRule Rule =
+ SarifRule::create().setRuleId(Name).setName(Name).setDescription(
+ Error.Message.Message);
if (!Name.starts_with("clang-diagnostic"))
Rule = Rule.setHelpURI(buildClangTidyHelpURI(Name));
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
index 23f88e7c5bd18..9cd4f0d4aa883 100644
--- a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
@@ -119,7 +119,7 @@ void f() {
//CHECK-SARIF-NEXT: "rank": -1
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "fullDescription": {
-//CHECK-SARIF-NEXT: "text": ""
+//CHECK-SARIF-NEXT: "text": "Dereference of null pointer (loaded from variable 'ptr')"
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "helpUri": "https://clang.llvm.org/extra/clang-tidy/checks/clang-analyzer/core.NullDereference.html",
//CHECK-SARIF-NEXT: "id": "clang-analyzer-core.NullDereference",
>From e5d69a0c326489fe0c2ebb4e58e64bcda7f5449d Mon Sep 17 00:00:00 2001
From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)"
<jones.matthew at siemens.com>
Date: Thu, 3 Sep 2026 07:55:56 -0700
Subject: [PATCH 08/12] added more in-depth test cases + for notes, removed
fullDescription and helpUri
---
clang-tools-extra/clang-tidy/ClangTidy.cpp | 53 +++--
.../clang-tidy/tool/ClangTidyMain.cpp | 8 +-
.../export-SARIF-Thread-flows.cpp | 21 +-
.../export-SARIF-diagnostics-terminal.cpp | 191 +++++++++++++++++-
.../export-SARIF-diagnostics.cpp | 143 ++++++++++++-
.../export-SARIF-no-location.cpp | 98 +++++++++
clang/include/clang/Basic/Sarif.h | 9 +-
clang/lib/Basic/Sarif.cpp | 16 +-
8 files changed, 472 insertions(+), 67 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-no-location.cpp
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index 29ca88680a7d9..530c9cfe8d491 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -215,6 +215,17 @@ class ErrorReporter {
exportSarifResult(Error, Loc);
}
+ CharSourceRange getNoteRange(const tooling::DiagnosticMessage &Note) {
+ if (!Note.Ranges.empty())
+ return getRange(Note.Ranges.front());
+
+ tooling::FileByteRange FBR;
+ FBR.FilePath = Note.FilePath;
+ FBR.FileOffset = Note.FileOffset;
+ FBR.Length = 1;
+ return getRange(FBR);
+ }
+
SmallVector<ThreadFlow, 8> createThreadFlows(const ClangTidyError &Error) {
SmallVector<ThreadFlow, 8> Flows;
if (!StringRef(Error.DiagnosticName).starts_with("clang-analyzer-"))
@@ -222,30 +233,13 @@ class ErrorReporter {
Flows.reserve(Error.Notes.size());
for (const tooling::DiagnosticMessage &Note : Error.Notes) {
- tooling::FileByteRange FBR;
- if (Note.Ranges.empty()) {
- FBR.FilePath = Note.FilePath;
- FBR.FileOffset = Note.FileOffset;
- FBR.Length = 1;
- } else {
- FBR = Note.Ranges.front();
- }
- const CharSourceRange Range = getRange(FBR);
- Flows.push_back(
- ThreadFlow::create().setRange(Range).setMessage(Note.Message));
+ Flows.push_back(ThreadFlow::create()
+ .setRange(getNoteRange(Note))
+ .setMessage(Note.Message));
}
return Flows;
}
- static std::string buildClangTidyHelpURI(StringRef Name) {
- const auto [Module, Check] = Name.starts_with("clang-analyzer-")
- ? Name.rsplit('-')
- : Name.split('-');
- return llvm::formatv(
- "https://clang.llvm.org/extra/clang-tidy/checks/{0}/{1}.html", Module,
- Check);
- }
-
void exportSarifResult(const ClangTidyError &Error,
const SourceLocation Loc) {
assert(SarifWriter &&
@@ -262,24 +256,27 @@ class ErrorReporter {
if (Inserted) {
const StringRef Name = Error.DiagnosticName;
- SarifRule Rule =
- SarifRule::create().setRuleId(Name).setName(Name).setDescription(
- Error.Message.Message);
- if (!Name.starts_with("clang-diagnostic"))
- Rule = Rule.setHelpURI(buildClangTidyHelpURI(Name));
-
+ SarifRule Rule = SarifRule::create().setRuleId(Name).setName(Name);
Rule = addDiagnosticLevelToRule(Rule, EffectiveLevel);
if (SarifWriter)
RuleIndex = SarifWriter->createRule(Rule);
}
- const SarifResult Result =
+ SarifResult Result =
SarifResult::create(RuleIndex)
.setDiagnosticMessage(Error.Message.Message)
.setDiagnosticLevel(getSarifResultLevel(EffectiveLevel))
- .setThreadFlows(createThreadFlows(Error))
.addLocations(getResultRanges(Error, Loc));
+ if (StringRef(Error.DiagnosticName).starts_with("clang-analyzer-")) {
+ Result = Result.setThreadFlows(createThreadFlows(Error));
+ } else {
+ for (const tooling::DiagnosticMessage &Note : Error.Notes) {
+ const CharSourceRange Range = getNoteRange(Note);
+ if (Range.isValid())
+ Result = Result.addRelatedLocations(Range, Note.Message);
+ }
+ }
if (SarifWriter)
SarifWriter->appendResult(Result);
}
diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
index 442f9bf3f9834..693d1d42345c9 100644
--- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
+++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
@@ -312,7 +312,7 @@ code with clang-apply-replacements.
cl::value_desc("filename"),
cl::cat(ClangTidyCategory));
-static cl::opt<std::string> ExportSarif("export-sarif", desc(R"(
+static cl::opt<std::string> SarifExport("sarif-export", desc(R"(
File in which to store diagnostics in SARIF format.
)"),
cl::value_desc("filename"),
@@ -661,9 +661,9 @@ int clangTidyMain(int argc, const char **argv) {
const SmallString<256> FilePath = makeAbsolute(FileName);
std::unique_ptr<llvm::raw_fd_ostream> SarifOS;
- if (!ExportSarif.empty()) {
+ if (!SarifExport.empty()) {
std::error_code EC;
- SarifOS = std::make_unique<llvm::raw_fd_ostream>(ExportSarif, EC,
+ SarifOS = std::make_unique<llvm::raw_fd_ostream>(SarifExport, EC,
llvm::sys::fs::OF_Text);
if (EC) {
llvm::errs() << "Error opening output file: " << EC.message() << '\n';
@@ -774,7 +774,7 @@ int clangTidyMain(int argc, const char **argv) {
handleErrors(Errors, Context, DisableFixes ? FB_NoFix : Behaviour,
WErrorCount, BaseFS, SarifOS.get(),
- SarifOS && ExportSarif == "-");
+ SarifOS && SarifExport == "-");
if (!ExportFixes.empty() && !Errors.empty()) {
std::error_code EC;
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
index 9cd4f0d4aa883..0e2fa2d0fc3be 100644
--- a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
@@ -1,5 +1,5 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t-input.cpp
-// RUN: clang-tidy %t-input.cpp -checks='-*,clang-analyzer*' -export-sarif=%t.sarif > %t.msg 2>&1
+// RUN: clang-tidy %t-input.cpp -checks='-*,clang-analyzer*' -sarif-export=%t.sarif > %t.msg 2>&1
// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MESSAGES %s -implicit-check-not='{{warning|error|note}}:'
// RUN: FileCheck -input-file=%t.sarif -check-prefix=CHECK-SARIF %s
void f() {
@@ -20,7 +20,7 @@ void f() {
//CHECK-SARIF-NEXT: "length": {{[0-9]+}},
//CHECK-SARIF-NEXT: "location": {
//CHECK-SARIF-NEXT: "index": 0,
-//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "mimeType": "text/plain",
//CHECK-SARIF-NEXT: "roles": [
@@ -45,7 +45,7 @@ void f() {
//CHECK-SARIF-NEXT: "physicalLocation": {
//CHECK-SARIF-NEXT: "artifactLocation": {
//CHECK-SARIF-NEXT: "index": 0,
-//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "region": {
//CHECK-SARIF-NEXT: "endColumn": 11,
@@ -65,7 +65,7 @@ void f() {
//CHECK-SARIF-NEXT: "physicalLocation": {
//CHECK-SARIF-NEXT: "artifactLocation": {
//CHECK-SARIF-NEXT: "index": 0,
-//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "region": {
//CHECK-SARIF-NEXT: "endColumn": 7,
@@ -75,8 +75,8 @@ void f() {
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: ]
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ]
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: ]
//CHECK-SARIF-NEXT: }
@@ -87,7 +87,7 @@ void f() {
//CHECK-SARIF-NEXT: "physicalLocation": {
//CHECK-SARIF-NEXT: "artifactLocation": {
//CHECK-SARIF-NEXT: "index": 0,
-//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "region": {
//CHECK-SARIF-NEXT: "endColumn": 7,
@@ -118,10 +118,6 @@ void f() {
//CHECK-SARIF-NEXT: "level": "warning",
//CHECK-SARIF-NEXT: "rank": -1
//CHECK-SARIF-NEXT: },
-//CHECK-SARIF-NEXT: "fullDescription": {
-//CHECK-SARIF-NEXT: "text": "Dereference of null pointer (loaded from variable 'ptr')"
-//CHECK-SARIF-NEXT: },
-//CHECK-SARIF-NEXT: "helpUri": "https://clang.llvm.org/extra/clang-tidy/checks/clang-analyzer/core.NullDereference.html",
//CHECK-SARIF-NEXT: "id": "clang-analyzer-core.NullDereference",
//CHECK-SARIF-NEXT: "name": "clang-analyzer-core.NullDereference"
//CHECK-SARIF-NEXT: }
@@ -132,5 +128,4 @@ void f() {
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: ],
//CHECK-SARIF-NEXT: "version": "{{.*}}"
-//CHECK-SARIF-NEXT: }
-
+//CHECK-SARIF-NEXT: }
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-terminal.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-terminal.cpp
index 52fb3fc3a639c..015eace602f2f 100644
--- a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-terminal.cpp
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-terminal.cpp
@@ -1,5 +1,5 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t-input.cpp
-// RUN: not clang-tidy %t-input.cpp -checks='-*,google-explicit-constructor,clang-diagnostic-missing-prototypes,clang-diagnostic-zero-length-array' --warnings-as-errors='clang-diagnostic-missing-prototypes,google-explicit-constructor' -export-sarif - -- -Wmissing-prototypes -Wzero-length-array 2>/dev/null | FileCheck %s
+// RUN: not clang-tidy %t-input.cpp -checks='-*,google-explicit-constructor,clang-diagnostic-missing-prototypes,clang-diagnostic-zero-length-array' --warnings-as-errors='clang-diagnostic-missing-prototypes,google-explicit-constructor' -sarif-export - -- -Wmissing-prototypes -Wzero-length-array 2>/dev/null | FileCheck %s
#define X(n) void n ## n() {}
X(f)
int a[-1];
@@ -51,6 +51,59 @@ struct Foo {
//CHECK-NEXT: "message": {
//CHECK-NEXT: "text": "no previous prototype for function 'ff'"
//CHECK-NEXT: },
+//CHECK-NEXT: "relatedLocations": [
+//CHECK-NEXT: {
+//CHECK-NEXT: "message": {
+//CHECK-NEXT: "text": "expanded from macro 'X'"
+//CHECK-NEXT: },
+//CHECK-NEXT: "physicalLocation": {
+//CHECK-NEXT: "artifactLocation": {
+//CHECK-NEXT: "index": 0,
+//CHECK-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-NEXT: },
+//CHECK-NEXT: "region": {
+//CHECK-NEXT: "endColumn": 20,
+//CHECK-NEXT: "endLine": 1,
+//CHECK-NEXT: "startColumn": 19,
+//CHECK-NEXT: "startLine": 1
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: },
+//CHECK-NEXT: {
+//CHECK-NEXT: "message": {
+//CHECK-NEXT: "text": "declare 'static' if the function is not intended to be used outside of this translation unit"
+//CHECK-NEXT: },
+//CHECK-NEXT: "physicalLocation": {
+//CHECK-NEXT: "artifactLocation": {
+//CHECK-NEXT: "index": 0,
+//CHECK-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-NEXT: },
+//CHECK-NEXT: "region": {
+//CHECK-NEXT: "endColumn": 2,
+//CHECK-NEXT: "endLine": 2,
+//CHECK-NEXT: "startColumn": 1,
+//CHECK-NEXT: "startLine": 2
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: },
+//CHECK-NEXT: {
+//CHECK-NEXT: "message": {
+//CHECK-NEXT: "text": "expanded from macro 'X'"
+//CHECK-NEXT: },
+//CHECK-NEXT: "physicalLocation": {
+//CHECK-NEXT: "artifactLocation": {
+//CHECK-NEXT: "index": 0,
+//CHECK-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-NEXT: },
+//CHECK-NEXT: "region": {
+//CHECK-NEXT: "endColumn": 15,
+//CHECK-NEXT: "endLine": 1,
+//CHECK-NEXT: "startColumn": 14,
+//CHECK-NEXT: "startLine": 1
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: ],
//CHECK-NEXT: "ruleId": "clang-diagnostic-missing-prototypes",
//CHECK-NEXT: "ruleIndex": 0
//CHECK-NEXT: },
@@ -81,11 +134,11 @@ struct Foo {
//CHECK-NEXT: {
//CHECK-NEXT: "level": "warning",
//CHECK-NEXT: "locations": [
-//CHECK-NEXT: {
+//CHECK-NEXT: {
//CHECK-NEXT: "physicalLocation": {
//CHECK-NEXT: "artifactLocation": {
//CHECK-NEXT: "index": 0,
-//CHECK-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-NEXT: "uri": "file://{{.*}}-input.cpp"
//CHECK-NEXT: },
//CHECK-NEXT: "region": {
//CHECK-NEXT: "endColumn": 8,
@@ -95,4 +148,134 @@ struct Foo {
//CHECK-NEXT: }
//CHECK-NEXT: }
//CHECK-NEXT: }
-//CHECK-NEXT: ],
\ No newline at end of file
+//CHECK-NEXT: ],
+//CHECK-NEXT: "message": {
+//CHECK-NEXT: "text": "zero size arrays are an extension"
+//CHECK-NEXT: },
+//CHECK-NEXT: "ruleId": "clang-diagnostic-zero-length-array",
+//CHECK-NEXT: "ruleIndex": 2
+//CHECK-NEXT: },
+//CHECK-NEXT: {
+//CHECK-NEXT: "level": "error",
+//CHECK-NEXT: "locations": [
+//CHECK-NEXT: {
+//CHECK-NEXT: "physicalLocation": {
+//CHECK-NEXT: "artifactLocation": {
+//CHECK-NEXT: "index": 0,
+//CHECK-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-NEXT: },
+//CHECK-NEXT: "region": {
+//CHECK-NEXT: "endColumn": 12,
+//CHECK-NEXT: "endLine": 6,
+//CHECK-NEXT: "startColumn": 11,
+//CHECK-NEXT: "startLine": 6
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: ],
+//CHECK-NEXT: "message": {
+//CHECK-NEXT: "text": "unknown type name 'x'"
+//CHECK-NEXT: },
+//CHECK-NEXT: "ruleId": "clang-diagnostic-error",
+//CHECK-NEXT: "ruleIndex": 1
+//CHECK-NEXT: },
+//CHECK-NEXT: {
+//CHECK-NEXT: "level": "error",
+//CHECK-NEXT: "locations": [
+//CHECK-NEXT: {
+//CHECK-NEXT: "physicalLocation": {
+//CHECK-NEXT: "artifactLocation": {
+//CHECK-NEXT: "index": 0,
+//CHECK-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-NEXT: },
+//CHECK-NEXT: "region": {
+//CHECK-NEXT: "endColumn": 4,
+//CHECK-NEXT: "endLine": 8,
+//CHECK-NEXT: "startColumn": 3,
+//CHECK-NEXT: "startLine": 8
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: ],
+//CHECK-NEXT: "message": {
+//CHECK-NEXT: "text": "a type specifier is required for all declarations"
+//CHECK-NEXT: },
+//CHECK-NEXT: "ruleId": "clang-diagnostic-error",
+//CHECK-NEXT: "ruleIndex": 1
+//CHECK-NEXT: },
+//CHECK-NEXT: {
+//CHECK-NEXT: "level": "error",
+//CHECK-NEXT: "locations": [
+//CHECK-NEXT: {
+//CHECK-NEXT: "physicalLocation": {
+//CHECK-NEXT: "artifactLocation": {
+//CHECK-NEXT: "index": 0,
+//CHECK-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-NEXT: },
+//CHECK-NEXT: "region": {
+//CHECK-NEXT: "endColumn": 4,
+//CHECK-NEXT: "endLine": 9,
+//CHECK-NEXT: "startColumn": 3,
+//CHECK-NEXT: "startLine": 9
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: ],
+//CHECK-NEXT: "message": {
+//CHECK-NEXT: "text": "single-argument constructors must be marked explicit to avoid unintentional implicit conversions"
+//CHECK-NEXT: },
+//CHECK-NEXT: "ruleId": "google-explicit-constructor",
+//CHECK-NEXT: "ruleIndex": 3
+//CHECK-NEXT: }
+//CHECK-NEXT: ],
+//CHECK-NEXT: "tool": {
+//CHECK-NEXT: "driver": {
+//CHECK-NEXT: "fullName": "clang-tidy",
+//CHECK-NEXT: "informationUri": "https://clang.llvm.org/extra/clang-tidy/",
+//CHECK-NEXT: "language": "en-US",
+//CHECK-NEXT: "name": "clang-tidy",
+//CHECK-NEXT: "rules": [
+//CHECK-NEXT: {
+//CHECK-NEXT: "defaultConfiguration": {
+//CHECK-NEXT: "enabled": true,
+//CHECK-NEXT: "level": "error",
+//CHECK-NEXT: "rank": 50
+//CHECK-NEXT: },
+//CHECK-NEXT: "id": "clang-diagnostic-missing-prototypes",
+//CHECK-NEXT: "name": "clang-diagnostic-missing-prototypes"
+//CHECK-NEXT: },
+//CHECK-NEXT: {
+//CHECK-NEXT: "defaultConfiguration": {
+//CHECK-NEXT: "enabled": true,
+//CHECK-NEXT: "level": "error",
+//CHECK-NEXT: "rank": 50
+//CHECK-NEXT: },
+//CHECK-NEXT: "id": "clang-diagnostic-error",
+//CHECK-NEXT: "name": "clang-diagnostic-error"
+//CHECK-NEXT: },
+//CHECK-NEXT: {
+//CHECK-NEXT: "defaultConfiguration": {
+//CHECK-NEXT: "enabled": true,
+//CHECK-NEXT: "level": "warning",
+//CHECK-NEXT: "rank": -1
+//CHECK-NEXT: },
+//CHECK-NEXT: "id": "clang-diagnostic-zero-length-array",
+//CHECK-NEXT: "name": "clang-diagnostic-zero-length-array"
+//CHECK-NEXT: },
+//CHECK-NEXT: {
+//CHECK-NEXT: "defaultConfiguration": {
+//CHECK-NEXT: "enabled": true,
+//CHECK-NEXT: "level": "error",
+//CHECK-NEXT: "rank": 50
+//CHECK-NEXT: },
+//CHECK-NEXT: "id": "google-explicit-constructor",
+//CHECK-NEXT: "name": "google-explicit-constructor"
+//CHECK-NEXT: }
+//CHECK-NEXT: ],
+//CHECK-NEXT: "version": "{{.*}}"
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: }
+//CHECK-NEXT: ],
+//CHECK-NEXT: "version": "{{.*}}"
+//CHECK-NEXT: }
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics.cpp
index 4ba2d3d55a4ed..5ff0c5f9d9454 100644
--- a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics.cpp
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics.cpp
@@ -1,5 +1,5 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t-input.cpp
-// RUN: not clang-tidy %t-input.cpp -checks='-*,google-explicit-constructor,clang-diagnostic-missing-prototypes,clang-diagnostic-zero-length-array' --warnings-as-errors='clang-diagnostic-missing-prototypes,google-explicit-constructor' -export-sarif=%t.sarif -- -Wmissing-prototypes -Wzero-length-array > %t.msg 2>&1
+// RUN: not clang-tidy %t-input.cpp -checks='-*,google-explicit-constructor,clang-diagnostic-missing-prototypes,clang-diagnostic-zero-length-array' --warnings-as-errors='clang-diagnostic-missing-prototypes,google-explicit-constructor' -sarif-export=%t.sarif -- -Wmissing-prototypes -Wzero-length-array > %t.msg 2>&1
// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MESSAGES %s -implicit-check-not='{{warning|error|note}}:'
// RUN: FileCheck -input-file=%t.sarif -check-prefix=CHECK-SARIF %s
#define X(n) void n ## n() {}
@@ -33,7 +33,7 @@ struct Foo {
//CHECK-SARIF-NEXT: "length": {{[0-9]+}},
//CHECK-SARIF-NEXT: "location": {
//CHECK-SARIF-NEXT: "index": 0,
-//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "mimeType": "text/plain",
//CHECK-SARIF-NEXT: "roles": [
@@ -50,7 +50,7 @@ struct Foo {
//CHECK-SARIF-NEXT: "physicalLocation": {
//CHECK-SARIF-NEXT: "artifactLocation": {
//CHECK-SARIF-NEXT: "index": 0,
-//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "region": {
//CHECK-SARIF-NEXT: "endColumn": 2,
@@ -64,6 +64,59 @@ struct Foo {
//CHECK-SARIF-NEXT: "message": {
//CHECK-SARIF-NEXT: "text": "no previous prototype for function 'ff'"
//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "relatedLocations": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "expanded from macro 'X'"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 20,
+//CHECK-SARIF-NEXT: "endLine": 1,
+//CHECK-SARIF-NEXT: "startColumn": 19,
+//CHECK-SARIF-NEXT: "startLine": 1
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "declare 'static' if the function is not intended to be used outside of this translation unit"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 2,
+//CHECK-SARIF-NEXT: "endLine": 2,
+//CHECK-SARIF-NEXT: "startColumn": 1,
+//CHECK-SARIF-NEXT: "startLine": 2
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "expanded from macro 'X'"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 15,
+//CHECK-SARIF-NEXT: "endLine": 1,
+//CHECK-SARIF-NEXT: "startColumn": 14,
+//CHECK-SARIF-NEXT: "startLine": 1
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
//CHECK-SARIF-NEXT: "ruleId": "clang-diagnostic-missing-prototypes",
//CHECK-SARIF-NEXT: "ruleIndex": 0
//CHECK-SARIF-NEXT: },
@@ -74,7 +127,7 @@ struct Foo {
//CHECK-SARIF-NEXT: "physicalLocation": {
//CHECK-SARIF-NEXT: "artifactLocation": {
//CHECK-SARIF-NEXT: "index": 0,
-//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "region": {
//CHECK-SARIF-NEXT: "endColumn": 9,
@@ -98,7 +151,7 @@ struct Foo {
//CHECK-SARIF-NEXT: "physicalLocation": {
//CHECK-SARIF-NEXT: "artifactLocation": {
//CHECK-SARIF-NEXT: "index": 0,
-//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "region": {
//CHECK-SARIF-NEXT: "endColumn": 8,
@@ -122,7 +175,7 @@ struct Foo {
//CHECK-SARIF-NEXT: "physicalLocation": {
//CHECK-SARIF-NEXT: "artifactLocation": {
//CHECK-SARIF-NEXT: "index": 0,
-//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "region": {
//CHECK-SARIF-NEXT: "endColumn": 12,
@@ -146,7 +199,7 @@ struct Foo {
//CHECK-SARIF-NEXT: "physicalLocation": {
//CHECK-SARIF-NEXT: "artifactLocation": {
//CHECK-SARIF-NEXT: "index": 0,
-//CHECK-SARIF-NEXT: "uri": "{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "region": {
//CHECK-SARIF-NEXT: "endColumn": 4,
@@ -163,3 +216,79 @@ struct Foo {
//CHECK-SARIF-NEXT: "ruleId": "clang-diagnostic-error",
//CHECK-SARIF-NEXT: "ruleIndex": 1
//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "level": "error",
+//CHECK-SARIF-NEXT: "locations": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 4,
+//CHECK-SARIF-NEXT: "endLine": 9,
+//CHECK-SARIF-NEXT: "startColumn": 3,
+//CHECK-SARIF-NEXT: "startLine": 9
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "single-argument constructors must be marked explicit to avoid unintentional implicit conversions"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "ruleId": "google-explicit-constructor",
+//CHECK-SARIF-NEXT: "ruleIndex": 3
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "tool": {
+//CHECK-SARIF-NEXT: "driver": {
+//CHECK-SARIF-NEXT: "fullName": "clang-tidy",
+//CHECK-SARIF-NEXT: "informationUri": "https://clang.llvm.org/extra/clang-tidy/",
+//CHECK-SARIF-NEXT: "language": "en-US",
+//CHECK-SARIF-NEXT: "name": "clang-tidy",
+//CHECK-SARIF-NEXT: "rules": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "defaultConfiguration": {
+//CHECK-SARIF-NEXT: "enabled": true,
+//CHECK-SARIF-NEXT: "level": "error",
+//CHECK-SARIF-NEXT: "rank": 50
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "id": "clang-diagnostic-missing-prototypes",
+//CHECK-SARIF-NEXT: "name": "clang-diagnostic-missing-prototypes"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "defaultConfiguration": {
+//CHECK-SARIF-NEXT: "enabled": true,
+//CHECK-SARIF-NEXT: "level": "error",
+//CHECK-SARIF-NEXT: "rank": 50
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "id": "clang-diagnostic-error",
+//CHECK-SARIF-NEXT: "name": "clang-diagnostic-error"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "defaultConfiguration": {
+//CHECK-SARIF-NEXT: "enabled": true,
+//CHECK-SARIF-NEXT: "level": "warning",
+//CHECK-SARIF-NEXT: "rank": -1
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "id": "clang-diagnostic-zero-length-array",
+//CHECK-SARIF-NEXT: "name": "clang-diagnostic-zero-length-array"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "defaultConfiguration": {
+//CHECK-SARIF-NEXT: "enabled": true,
+//CHECK-SARIF-NEXT: "level": "error",
+//CHECK-SARIF-NEXT: "rank": 50
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "id": "google-explicit-constructor",
+//CHECK-SARIF-NEXT: "name": "google-explicit-constructor"
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "version": "{{.*}}"
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "version": "{{.*}}"
+//CHECK-SARIF-NEXT: }
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-no-location.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-no-location.cpp
new file mode 100644
index 0000000000000..b9855914cca6b
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-no-location.cpp
@@ -0,0 +1,98 @@
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t-input.cpp
+// RUN: not clang-tidy %t-input.cpp -checks='-*,clang-diagnostic-*,google-explicit-constructor' -sarif-export=%t.sarif -- -fake-command > %t.msg 2>&1
+// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MESSAGES %s -implicit-check-not='{{warning|error|note}}:'
+// RUN: FileCheck -input-file=%t.sarif -check-prefix=CHECK-SARIF %s
+class A { A(int) {} };
+
+//NOTE: "-fake-command" is rejected by the driver before a SourceManager is created,
+//NOTE: so the diagnostic is stored with an empty FilePath and ranges. Therefore, getResultRanges()
+//NOTE: omits locations entirely.
+
+//CHECK-MESSAGES: error: unknown argument: '-fake-command' [clang-diagnostic-error]
+//CHECK-MESSAGES: -input.cpp:1:11: warning: single-argument constructors must be marked explicit to avoid unintentional implicit conversions [google-explicit-constructor]
+
+//CHECK-SARIF: {
+//CHECK-SARIF-NEXT: "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json",
+//CHECK-SARIF-NEXT: "runs": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "artifacts": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "length": {{[0-9]+}},
+//CHECK-SARIF-NEXT: "location": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "mimeType": "text/plain",
+//CHECK-SARIF-NEXT: "roles": [
+//CHECK-SARIF-NEXT: "resultFile"
+//CHECK-SARIF-NEXT: ]
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "columnKind": "unicodeCodePoints",
+//CHECK-SARIF-NEXT: "results": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "level": "error",
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "unknown argument: '-fake-command'"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "ruleId": "clang-diagnostic-error",
+//CHECK-SARIF-NEXT: "ruleIndex": 0
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "level": "warning",
+//CHECK-SARIF-NEXT: "locations": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 12,
+//CHECK-SARIF-NEXT: "endLine": 1,
+//CHECK-SARIF-NEXT: "startColumn": 11,
+//CHECK-SARIF-NEXT: "startLine": 1
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "single-argument constructors must be marked explicit to avoid unintentional implicit conversions"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "ruleId": "google-explicit-constructor",
+//CHECK-SARIF-NEXT: "ruleIndex": 1
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "tool": {
+//CHECK-SARIF-NEXT: "driver": {
+//CHECK-SARIF-NEXT: "fullName": "clang-tidy",
+//CHECK-SARIF-NEXT: "informationUri": "https://clang.llvm.org/extra/clang-tidy/",
+//CHECK-SARIF-NEXT: "language": "en-US",
+//CHECK-SARIF-NEXT: "name": "clang-tidy",
+//CHECK-SARIF-NEXT: "rules": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "defaultConfiguration": {
+//CHECK-SARIF-NEXT: "enabled": true,
+//CHECK-SARIF-NEXT: "level": "error",
+//CHECK-SARIF-NEXT: "rank": 50
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "id": "clang-diagnostic-error",
+//CHECK-SARIF-NEXT: "name": "clang-diagnostic-error"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "defaultConfiguration": {
+//CHECK-SARIF-NEXT: "enabled": true,
+//CHECK-SARIF-NEXT: "level": "warning",
+//CHECK-SARIF-NEXT: "rank": -1
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "id": "google-explicit-constructor",
+//CHECK-SARIF-NEXT: "name": "google-explicit-constructor"
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "version": "{{.*}}"
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "version": "{{.*}}"
+//CHECK-SARIF-NEXT: }
diff --git a/clang/include/clang/Basic/Sarif.h b/clang/include/clang/Basic/Sarif.h
index 7651d2ac7a768..231f28599d785 100644
--- a/clang/include/clang/Basic/Sarif.h
+++ b/clang/include/clang/Basic/Sarif.h
@@ -332,7 +332,8 @@ class SarifResult {
std::string HostedViewerURI;
llvm::SmallDenseMap<StringRef, std::string, 4> PartialFingerprints;
llvm::SmallVector<CharSourceRange, 8> Locations;
- llvm::SmallVector<CharSourceRange, 8> RelatedLocations;
+ llvm::SmallVector<std::pair<CharSourceRange, std::string>, 8>
+ RelatedLocations;
llvm::SmallVector<ThreadFlow, 8> ThreadFlows;
std::optional<SarifResultLevel> LevelOverride;
@@ -373,7 +374,8 @@ class SarifResult {
return *this;
}
- SarifResult addRelatedLocations(llvm::ArrayRef<CharSourceRange> DiagLocs) {
+ SarifResult addRelatedLocations(llvm::ArrayRef<CharSourceRange> DiagLocs,
+ llvm::StringRef Message = "") {
#ifndef NDEBUG
for (const auto &Loc : DiagLocs) {
assert(
@@ -381,7 +383,8 @@ class SarifResult {
"SARIF RelatedLocations require character granular source ranges!");
}
#endif
- RelatedLocations.append(DiagLocs.begin(), DiagLocs.end());
+ for (const CharSourceRange &Loc : DiagLocs)
+ RelatedLocations.emplace_back(Loc, Message.str());
return *this;
}
diff --git a/clang/lib/Basic/Sarif.cpp b/clang/lib/Basic/Sarif.cpp
index 5536e6a02091f..a6e154beb0684 100644
--- a/clang/lib/Basic/Sarif.cpp
+++ b/clang/lib/Basic/Sarif.cpp
@@ -276,11 +276,11 @@ void SarifDocumentWriter::endRun() {
{"enabled", R.DefaultConfiguration.Enabled},
{"level", resultLevelToStr(R.DefaultConfiguration.Level)},
{"rank", R.DefaultConfiguration.Rank}};
- json::Object Rule{
- {"name", R.Name},
- {"id", R.Id},
- {"fullDescription", json::Object{{"text", R.Description}}},
- {"defaultConfiguration", std::move(Config)}};
+ json::Object Rule{{"name", R.Name},
+ {"id", R.Id},
+ {"defaultConfiguration", std::move(Config)}};
+ if (!(Tool.getObject("driver")->getString("fullName") == "clang-tidy"))
+ Rule["fullDescription"] = json::Object{{"text", R.Description}};
if (!R.HelpURI.empty())
Rule["helpUri"] = R.HelpURI;
if (!R.DeprecatedIds.empty())
@@ -411,9 +411,9 @@ void SarifDocumentWriter::appendResult(const SarifResult &Result) {
if (!Result.RelatedLocations.empty()) {
json::Array ReLocs;
- for (auto &Range : Result.RelatedLocations) {
- ReLocs.emplace_back(createLocation(createPhysicalLocation(Range)));
- }
+ for (const auto &[Range, Message] : Result.RelatedLocations)
+ ReLocs.emplace_back(
+ createLocation(createPhysicalLocation(Range), Message));
Ret["relatedLocations"] = std::move(ReLocs);
}
>From c21c79856b3539cdb3830d5574164f5cdb78327d Mon Sep 17 00:00:00 2001
From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)"
<jones.matthew at siemens.com>
Date: Wed, 9 Sep 2026 08:04:55 -0700
Subject: [PATCH 09/12] formatting
---
clang/include/clang/Basic/Sarif.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/Sarif.h b/clang/include/clang/Basic/Sarif.h
index 231f28599d785..18727736f5372 100644
--- a/clang/include/clang/Basic/Sarif.h
+++ b/clang/include/clang/Basic/Sarif.h
@@ -374,7 +374,7 @@ class SarifResult {
return *this;
}
- SarifResult addRelatedLocations(llvm::ArrayRef<CharSourceRange> DiagLocs,
+ SarifResult addRelatedLocations(llvm::ArrayRef<CharSourceRange> DiagLocs,
llvm::StringRef Message = "") {
#ifndef NDEBUG
for (const auto &Loc : DiagLocs) {
>From d0fe1988b1977c1c2caf443b59c4b6925cedf6b5 Mon Sep 17 00:00:00 2001
From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)"
<jones.matthew at siemens.com>
Date: Mon, 28 Sep 2026 07:53:24 -0700
Subject: [PATCH 10/12] [clang-tidy] fixed macro expansions, included new test,
modified release notes, fixed explicitly naming clang-tidy tooling name
---
clang-tools-extra/clang-tidy/ClangTidy.cpp | 3 +-
.../ClangTidyDiagnosticConsumer.cpp | 38 ++--
clang-tools-extra/docs/ReleaseNotes.md | 4 +-
.../export-SARIF-Thread-flows.cpp | 3 +
.../export-SARIF-diagnostics-macro.cpp | 179 ++++++++++++++++++
.../export-SARIF-diagnostics-terminal.cpp | 12 ++
.../export-SARIF-diagnostics.cpp | 12 ++
.../export-SARIF-no-location.cpp | 6 +
clang/include/clang/Basic/Sarif.h | 3 +-
clang/lib/Basic/Sarif.cpp | 23 +--
10 files changed, 245 insertions(+), 38 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-macro.cpp
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index 530c9cfe8d491..0adbc6bae024a 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -117,7 +117,8 @@ class ErrorReporter {
SarifOS(SarifOS) {
if (SarifOS) {
SarifWriter.emplace(SourceMgr);
- SarifWriter->createRun("clang-tidy", "clang-tidy");
+ SarifWriter->createRun("clang-tidy", "clang-tidy", CLANG_VERSION_STRING,
+ "https://clang.llvm.org/extra/clang-tidy/");
}
DiagOpts.setShowColors(Context.getOptions().UseColor.value_or(
diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
index 61eacd5df6c25..c148056ddc22d 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
@@ -70,37 +70,29 @@ class ClangTidyDiagnosticRenderer : public DiagnosticRenderer {
? tooling::DiagnosticMessage(Message, Loc.getManager(), Loc)
: tooling::DiagnosticMessage(Message);
- // Make sure that if a TokenRange is received from the check it is unfurled
- // into a real CharRange for the diagnostic printer later.
- // Whatever we store here gets decoupled from the current SourceManager, so
- // we **have to** know the exact position and length of the highlight.
- const auto ToCharRange = [this, &Loc](const CharSourceRange &SourceRange) {
- if (SourceRange.isCharRange())
- return SourceRange;
- assert(SourceRange.isTokenRange());
- const SourceLocation End = Lexer::getLocForEndOfToken(
- SourceRange.getEnd(), 0, Loc.getManager(), LangOpts);
- return CharSourceRange::getCharRange(SourceRange.getBegin(), End);
+ // Whatever we store here gets decoupled from the current Source Manager, so
+ // each range must become a character range in a real file now.
+ // makeFileCharRange maps a range covering a whole macro expansion to the
+ // macro call, and a range inside a macro argument to where the argument is
+ // written. Ranges it cannot map (invalid, or only part of a macro body)
+ // come back invalid and are dropped.
+ const auto StoreRanges = [&](tooling::DiagnosticMessage &Stored) {
+ for (const CharSourceRange &Range : Ranges) {
+ const CharSourceRange FileRange =
+ Lexer::makeFileCharRange(Range, Loc.getManager(), LangOpts);
+ if (FileRange.isValid())
+ Stored.Ranges.emplace_back(Loc.getManager(), FileRange);
+ }
};
- // We are only interested in valid ranges.
- const auto ValidRanges =
- llvm::make_filter_range(Ranges, [](const CharSourceRange &R) {
- return R.getAsRange().isValid();
- });
-
if (Level == DiagnosticsEngine::Note) {
Error.Notes.push_back(TidyMessage);
- for (const CharSourceRange &SourceRange : ValidRanges)
- Error.Notes.back().Ranges.emplace_back(Loc.getManager(),
- ToCharRange(SourceRange));
+ StoreRanges(Error.Notes.back());
return;
}
assert(Error.Message.Message.empty() && "Overwriting a diagnostic message");
Error.Message = TidyMessage;
- for (const CharSourceRange &SourceRange : ValidRanges)
- Error.Message.Ranges.emplace_back(Loc.getManager(),
- ToCharRange(SourceRange));
+ StoreRanges(Error.Message);
}
void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
diff --git a/clang-tools-extra/docs/ReleaseNotes.md b/clang-tools-extra/docs/ReleaseNotes.md
index 4c9f10c4c7153..e9b5362bf1c2f 100644
--- a/clang-tools-extra/docs/ReleaseNotes.md
+++ b/clang-tools-extra/docs/ReleaseNotes.md
@@ -105,8 +105,8 @@ infrastructure are described first, followed by tool-specific sections.
piping from {program}`git` to {program}`clang-tidy-diff.py`, where slashes
will now be automatically normalized.
-- Added a new {program}`clang-tidy` command line option `-export-sarif=<file>`
- in which clang-tidy diagnostics are exported in the `SARIF https://sarifweb.azurewebsites.net/`
+- Added a new {program}`clang-tidy` command line option `-sarif-export=<file>`
+ in which clang-tidy diagnostics are exported in the [SARIF](https://sarifweb.azurewebsites.net/)
format.
#### New checks
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
index 0e2fa2d0fc3be..8891635865acc 100644
--- a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
@@ -118,6 +118,9 @@ void f() {
//CHECK-SARIF-NEXT: "level": "warning",
//CHECK-SARIF-NEXT: "rank": -1
//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "fullDescription": {
+//CHECK-SARIF-NEXT: "text": ""
+//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "id": "clang-analyzer-core.NullDereference",
//CHECK-SARIF-NEXT: "name": "clang-analyzer-core.NullDereference"
//CHECK-SARIF-NEXT: }
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-macro.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-macro.cpp
new file mode 100644
index 0000000000000..cedbaf62c8858
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-macro.cpp
@@ -0,0 +1,179 @@
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t-input.cpp
+// RUN: clang-tidy %t-input.cpp -checks='-*,clang-analyzer-*' -sarif-export=%t.sarif > %t.msg 2>&1
+// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MESSAGES %s -implicit-check-not='{{warning|error|note}}:'
+// RUN: FileCheck -input-file=%t.sarif -check-prefix=CHECK-SARIF %s
+#define DEREF(p) (*(p))
+#define NUL nullptr
+
+int g() {
+ int *ptr = NUL;
+ return DEREF(ptr);
+}
+
+//CHECK-MESSAGES: -input.cpp:6:10: warning: Dereference of null pointer (loaded from variable 'ptr') [clang-analyzer-core.NullDereference]
+//CHECK-MESSAGES: -input.cpp:1:18: note: expanded from macro 'DEREF'
+//CHECK-MESSAGES: -input.cpp:5:3: note: 'ptr' initialized to a null pointer value
+//CHECK-MESSAGES: -input.cpp:6:10: note: Dereference of null pointer (loaded from variable 'ptr')
+//CHECK-MESSAGES: -input.cpp:1:18: note: expanded from macro 'DEREF'
+
+//CHECK-SARIF: {
+//CHECK-SARIF-NEXT: "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json",
+//CHECK-SARIF-NEXT: "runs": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "artifacts": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "length": {{[0-9]+}},
+//CHECK-SARIF-NEXT: "location": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "mimeType": "text/plain",
+//CHECK-SARIF-NEXT: "roles": [
+//CHECK-SARIF-NEXT: "resultFile"
+//CHECK-SARIF-NEXT: ]
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "columnKind": "unicodeCodePoints",
+//CHECK-SARIF-NEXT: "results": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "codeFlows": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "threadFlows": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "locations": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "importance": "important",
+//CHECK-SARIF-NEXT: "location": {
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "expanded from macro 'DEREF'"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 22,
+//CHECK-SARIF-NEXT: "endLine": 1,
+//CHECK-SARIF-NEXT: "startColumn": 21,
+//CHECK-SARIF-NEXT: "startLine": 1
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "importance": "important",
+//CHECK-SARIF-NEXT: "location": {
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "'ptr' initialized to a null pointer value"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 11,
+//CHECK-SARIF-NEXT: "endLine": 5,
+//CHECK-SARIF-NEXT: "startColumn": 3,
+//CHECK-SARIF-NEXT: "startLine": 5
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "importance": "important",
+//CHECK-SARIF-NEXT: "location": {
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "Dereference of null pointer (loaded from variable 'ptr')"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 19,
+//CHECK-SARIF-NEXT: "endLine": 6,
+//CHECK-SARIF-NEXT: "startColumn": 16,
+//CHECK-SARIF-NEXT: "startLine": 6
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "importance": "important",
+//CHECK-SARIF-NEXT: "location": {
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "expanded from macro 'DEREF'"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 22,
+//CHECK-SARIF-NEXT: "endLine": 1,
+//CHECK-SARIF-NEXT: "startColumn": 21,
+//CHECK-SARIF-NEXT: "startLine": 1
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ]
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ]
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "level": "warning",
+//CHECK-SARIF-NEXT: "locations": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 19,
+//CHECK-SARIF-NEXT: "endLine": 6,
+//CHECK-SARIF-NEXT: "startColumn": 16,
+//CHECK-SARIF-NEXT: "startLine": 6
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "Dereference of null pointer (loaded from variable 'ptr')"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "ruleId": "clang-analyzer-core.NullDereference",
+//CHECK-SARIF-NEXT: "ruleIndex": 0
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "tool": {
+//CHECK-SARIF-NEXT: "driver": {
+//CHECK-SARIF-NEXT: "fullName": "clang-tidy",
+//CHECK-SARIF-NEXT: "informationUri": "https://clang.llvm.org/extra/clang-tidy/",
+//CHECK-SARIF-NEXT: "language": "en-US",
+//CHECK-SARIF-NEXT: "name": "clang-tidy",
+//CHECK-SARIF-NEXT: "rules": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "defaultConfiguration": {
+//CHECK-SARIF-NEXT: "enabled": true,
+//CHECK-SARIF-NEXT: "level": "warning",
+//CHECK-SARIF-NEXT: "rank": -1
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "fullDescription": {
+//CHECK-SARIF-NEXT: "text": ""
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "id": "clang-analyzer-core.NullDereference",
+//CHECK-SARIF-NEXT: "name": "clang-analyzer-core.NullDereference"
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "version": "24.0.0git"
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "version": "2.1.0"
+//CHECK-SARIF-NEXT: }
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-terminal.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-terminal.cpp
index 015eace602f2f..3dbe5f28ab985 100644
--- a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-terminal.cpp
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-terminal.cpp
@@ -241,6 +241,9 @@ struct Foo {
//CHECK-NEXT: "level": "error",
//CHECK-NEXT: "rank": 50
//CHECK-NEXT: },
+//CHECK-NEXT: "fullDescription": {
+//CHECK-NEXT: "text": ""
+//CHECK-NEXT: },
//CHECK-NEXT: "id": "clang-diagnostic-missing-prototypes",
//CHECK-NEXT: "name": "clang-diagnostic-missing-prototypes"
//CHECK-NEXT: },
@@ -250,6 +253,9 @@ struct Foo {
//CHECK-NEXT: "level": "error",
//CHECK-NEXT: "rank": 50
//CHECK-NEXT: },
+//CHECK-NEXT: "fullDescription": {
+//CHECK-NEXT: "text": ""
+//CHECK-NEXT: },
//CHECK-NEXT: "id": "clang-diagnostic-error",
//CHECK-NEXT: "name": "clang-diagnostic-error"
//CHECK-NEXT: },
@@ -259,6 +265,9 @@ struct Foo {
//CHECK-NEXT: "level": "warning",
//CHECK-NEXT: "rank": -1
//CHECK-NEXT: },
+//CHECK-NEXT: "fullDescription": {
+//CHECK-NEXT: "text": ""
+//CHECK-NEXT: },
//CHECK-NEXT: "id": "clang-diagnostic-zero-length-array",
//CHECK-NEXT: "name": "clang-diagnostic-zero-length-array"
//CHECK-NEXT: },
@@ -268,6 +277,9 @@ struct Foo {
//CHECK-NEXT: "level": "error",
//CHECK-NEXT: "rank": 50
//CHECK-NEXT: },
+//CHECK-NEXT: "fullDescription": {
+//CHECK-NEXT: "text": ""
+//CHECK-NEXT: },
//CHECK-NEXT: "id": "google-explicit-constructor",
//CHECK-NEXT: "name": "google-explicit-constructor"
//CHECK-NEXT: }
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics.cpp
index 5ff0c5f9d9454..7ea3beff3d811 100644
--- a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics.cpp
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics.cpp
@@ -254,6 +254,9 @@ struct Foo {
//CHECK-SARIF-NEXT: "level": "error",
//CHECK-SARIF-NEXT: "rank": 50
//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "fullDescription": {
+//CHECK-SARIF-NEXT: "text": ""
+//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "id": "clang-diagnostic-missing-prototypes",
//CHECK-SARIF-NEXT: "name": "clang-diagnostic-missing-prototypes"
//CHECK-SARIF-NEXT: },
@@ -263,6 +266,9 @@ struct Foo {
//CHECK-SARIF-NEXT: "level": "error",
//CHECK-SARIF-NEXT: "rank": 50
//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "fullDescription": {
+//CHECK-SARIF-NEXT: "text": ""
+//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "id": "clang-diagnostic-error",
//CHECK-SARIF-NEXT: "name": "clang-diagnostic-error"
//CHECK-SARIF-NEXT: },
@@ -272,6 +278,9 @@ struct Foo {
//CHECK-SARIF-NEXT: "level": "warning",
//CHECK-SARIF-NEXT: "rank": -1
//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "fullDescription": {
+//CHECK-SARIF-NEXT: "text": ""
+//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "id": "clang-diagnostic-zero-length-array",
//CHECK-SARIF-NEXT: "name": "clang-diagnostic-zero-length-array"
//CHECK-SARIF-NEXT: },
@@ -281,6 +290,9 @@ struct Foo {
//CHECK-SARIF-NEXT: "level": "error",
//CHECK-SARIF-NEXT: "rank": 50
//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "fullDescription": {
+//CHECK-SARIF-NEXT: "text": ""
+//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "id": "google-explicit-constructor",
//CHECK-SARIF-NEXT: "name": "google-explicit-constructor"
//CHECK-SARIF-NEXT: }
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-no-location.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-no-location.cpp
index b9855914cca6b..8e40667f73581 100644
--- a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-no-location.cpp
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-no-location.cpp
@@ -76,6 +76,9 @@ class A { A(int) {} };
//CHECK-SARIF-NEXT: "level": "error",
//CHECK-SARIF-NEXT: "rank": 50
//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "fullDescription": {
+//CHECK-SARIF-NEXT: "text": ""
+//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "id": "clang-diagnostic-error",
//CHECK-SARIF-NEXT: "name": "clang-diagnostic-error"
//CHECK-SARIF-NEXT: },
@@ -85,6 +88,9 @@ class A { A(int) {} };
//CHECK-SARIF-NEXT: "level": "warning",
//CHECK-SARIF-NEXT: "rank": -1
//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "fullDescription": {
+//CHECK-SARIF-NEXT: "text": ""
+//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "id": "google-explicit-constructor",
//CHECK-SARIF-NEXT: "name": "google-explicit-constructor"
//CHECK-SARIF-NEXT: }
diff --git a/clang/include/clang/Basic/Sarif.h b/clang/include/clang/Basic/Sarif.h
index 18727736f5372..35b07591b7ebb 100644
--- a/clang/include/clang/Basic/Sarif.h
+++ b/clang/include/clang/Basic/Sarif.h
@@ -472,7 +472,8 @@ class SarifDocumentWriter {
/// 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);
+ const llvm::StringRef ToolVersion = CLANG_VERSION_STRING,
+ const llvm::StringRef InformationUri = "");
/// If there is a current run, end it.
///
diff --git a/clang/lib/Basic/Sarif.cpp b/clang/lib/Basic/Sarif.cpp
index a6e154beb0684..cf5329603a17d 100644
--- a/clang/lib/Basic/Sarif.cpp
+++ b/clang/lib/Basic/Sarif.cpp
@@ -276,11 +276,11 @@ void SarifDocumentWriter::endRun() {
{"enabled", R.DefaultConfiguration.Enabled},
{"level", resultLevelToStr(R.DefaultConfiguration.Level)},
{"rank", R.DefaultConfiguration.Rank}};
- json::Object Rule{{"name", R.Name},
- {"id", R.Id},
- {"defaultConfiguration", std::move(Config)}};
- if (!(Tool.getObject("driver")->getString("fullName") == "clang-tidy"))
- Rule["fullDescription"] = json::Object{{"text", R.Description}};
+ json::Object Rule{
+ {"name", R.Name},
+ {"id", R.Id},
+ {"fullDescription", json::Object{{"text", R.Description}}},
+ {"defaultConfiguration", std::move(Config)}};
if (!R.HelpURI.empty())
Rule["helpUri"] = R.HelpURI;
if (!R.DeprecatedIds.empty())
@@ -344,22 +344,23 @@ SarifDocumentWriter::createCodeFlow(ArrayRef<ThreadFlow> ThreadFlows) {
void SarifDocumentWriter::createRun(StringRef ShortToolName,
StringRef LongToolName,
- StringRef ToolVersion) {
+ StringRef ToolVersion,
+ StringRef InformationUri) {
// Clear resources associated with a previous run.
endRun();
// Signify a new run has begun.
Closed = false;
-
- StringRef InformationUri = "https://clang.llvm.org/docs/UsersManual.html";
- if (LongToolName == "clang-tidy")
- InformationUri = "https://clang.llvm.org/extra/clang-tidy/";
+ StringRef EffectiveInformationUri =
+ InformationUri.empty() ? "https://clang.llvm.org/docs/UsersManual.html"
+ : InformationUri;
json::Object Tool{{"driver", json::Object{{"name", ShortToolName},
{"fullName", LongToolName},
{"language", "en-US"},
{"version", ToolVersion},
- {"informationUri", InformationUri},
+ {"informationUri",
+ EffectiveInformationUri},
{"rules", json::Array{}}}}};
json::Object TheRun{{"tool", std::move(Tool)},
{"results", {}},
>From a8caee557d9034f372682db0f60a55880963633b Mon Sep 17 00:00:00 2001
From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)"
<jones.matthew at siemens.com>
Date: Wed, 30 Sep 2026 06:31:52 -0700
Subject: [PATCH 11/12] [clang-tidy] Fixed EOF result ranges offset failing for
clang-tidy SARIF, updated SARIF tests
---
clang-tools-extra/clang-tidy/ClangTidy.cpp | 23 +--
.../export-SARIF-Thread-flows.cpp | 134 ------------------
.../export-SARIF-diagnostics-EOF.cpp | 78 ++++++++++
...-SARIF-diagnostics-macro-thread-flows.cpp} | 36 +++--
4 files changed, 108 insertions(+), 163 deletions(-)
delete mode 100644 clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-EOF.cpp
rename clang-tools-extra/test/clang-tidy/infrastructure/{export-SARIF-diagnostics-macro.cpp => export-SARIF-diagnostics-macro-thread-flows.cpp} (90%)
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index 0adbc6bae024a..feb4f3ac29159 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -219,12 +219,10 @@ class ErrorReporter {
CharSourceRange getNoteRange(const tooling::DiagnosticMessage &Note) {
if (!Note.Ranges.empty())
return getRange(Note.Ranges.front());
-
- tooling::FileByteRange FBR;
- FBR.FilePath = Note.FilePath;
- FBR.FileOffset = Note.FileOffset;
- FBR.Length = 1;
- return getRange(FBR);
+ const SourceLocation Loc = getLocation(Note.FilePath, Note.FileOffset);
+ if (Loc.isInvalid())
+ return {};
+ return getOffsetRange(SourceMgr, Loc);
}
SmallVector<ThreadFlow, 8> createThreadFlows(const ClangTidyError &Error) {
@@ -282,6 +280,14 @@ class ErrorReporter {
SarifWriter->appendResult(Result);
}
+ CharSourceRange getOffsetRange(SourceManager &SM, SourceLocation Loc) {
+ const std::pair<FileID, unsigned> &DecomposedLoc = SM.getDecomposedLoc(Loc);
+ const FileID FileId = DecomposedLoc.first;
+ const unsigned Offset = DecomposedLoc.second;
+ return CharSourceRange::getCharRange(
+ Loc, Loc.getLocWithOffset(Offset < SM.getFileIDSize(FileId) ? 1 : 0));
+ }
+
SmallVector<CharSourceRange, 4> getResultRanges(const ClangTidyError &Error,
SourceLocation Loc) {
SmallVector<CharSourceRange, 4> Ranges;
@@ -293,9 +299,8 @@ class ErrorReporter {
// Some Clang-Tidy diagnostics are issued with a single location (not a
// range). For these, we create a range of length 1 at the diagnostic
// location. As, SARIF results require a character range for each
- // location.
- Ranges.push_back(
- CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(1)));
+ // location, for EOF we create a zero-length range.
+ Ranges.push_back(getOffsetRange(SourceMgr, Loc));
}
return Ranges;
}
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
deleted file mode 100644
index 8891635865acc..0000000000000
--- a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-Thread-flows.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-// RUN: grep -Ev "// *[A-Z-]+:" %s > %t-input.cpp
-// RUN: clang-tidy %t-input.cpp -checks='-*,clang-analyzer*' -sarif-export=%t.sarif > %t.msg 2>&1
-// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MESSAGES %s -implicit-check-not='{{warning|error|note}}:'
-// RUN: FileCheck -input-file=%t.sarif -check-prefix=CHECK-SARIF %s
-void f() {
- int *ptr = nullptr;
- *ptr = 1;
-}
-
-//CHECK-MESSAGES: -input.cpp:3:8: warning: Dereference of null pointer (loaded from variable 'ptr') [clang-analyzer-core.NullDereference]
-//CHECK-MESSAGES: -input.cpp:2:3: note: 'ptr' initialized to a null pointer value
-//CHECK-MESSAGES: -input.cpp:3:8: note: Dereference of null pointer (loaded from variable 'ptr')
-
-//CHECK-SARIF: {
-//CHECK-SARIF-NEXT: "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json",
-//CHECK-SARIF-NEXT: "runs": [
-//CHECK-SARIF-NEXT: {
-//CHECK-SARIF-NEXT: "artifacts": [
-//CHECK-SARIF-NEXT: {
-//CHECK-SARIF-NEXT: "length": {{[0-9]+}},
-//CHECK-SARIF-NEXT: "location": {
-//CHECK-SARIF-NEXT: "index": 0,
-//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
-//CHECK-SARIF-NEXT: },
-//CHECK-SARIF-NEXT: "mimeType": "text/plain",
-//CHECK-SARIF-NEXT: "roles": [
-//CHECK-SARIF-NEXT: "resultFile"
-//CHECK-SARIF-NEXT: ]
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: ],
-//CHECK-SARIF-NEXT: "columnKind": "unicodeCodePoints",
-//CHECK-SARIF-NEXT: "results": [
-//CHECK-SARIF-NEXT: {
-//CHECK-SARIF-NEXT: "codeFlows": [
-//CHECK-SARIF-NEXT: {
-//CHECK-SARIF-NEXT: "threadFlows": [
-//CHECK-SARIF-NEXT: {
-//CHECK-SARIF-NEXT: "locations": [
-//CHECK-SARIF-NEXT: {
-//CHECK-SARIF-NEXT: "importance": "important",
-//CHECK-SARIF-NEXT: "location": {
-//CHECK-SARIF-NEXT: "message": {
-//CHECK-SARIF-NEXT: "text": "'ptr' initialized to a null pointer value"
-//CHECK-SARIF-NEXT: },
-//CHECK-SARIF-NEXT: "physicalLocation": {
-//CHECK-SARIF-NEXT: "artifactLocation": {
-//CHECK-SARIF-NEXT: "index": 0,
-//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
-//CHECK-SARIF-NEXT: },
-//CHECK-SARIF-NEXT: "region": {
-//CHECK-SARIF-NEXT: "endColumn": 11,
-//CHECK-SARIF-NEXT: "endLine": 2,
-//CHECK-SARIF-NEXT: "startColumn": 3,
-//CHECK-SARIF-NEXT: "startLine": 2
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: },
-//CHECK-SARIF-NEXT: {
-//CHECK-SARIF-NEXT: "importance": "important",
-//CHECK-SARIF-NEXT: "location": {
-//CHECK-SARIF-NEXT: "message": {
-//CHECK-SARIF-NEXT: "text": "Dereference of null pointer (loaded from variable 'ptr')"
-//CHECK-SARIF-NEXT: },
-//CHECK-SARIF-NEXT: "physicalLocation": {
-//CHECK-SARIF-NEXT: "artifactLocation": {
-//CHECK-SARIF-NEXT: "index": 0,
-//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
-//CHECK-SARIF-NEXT: },
-//CHECK-SARIF-NEXT: "region": {
-//CHECK-SARIF-NEXT: "endColumn": 7,
-//CHECK-SARIF-NEXT: "endLine": 3,
-//CHECK-SARIF-NEXT: "startColumn": 4,
-//CHECK-SARIF-NEXT: "startLine": 3
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: ]
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: ]
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: ],
-//CHECK-SARIF-NEXT: "level": "warning",
-//CHECK-SARIF-NEXT: "locations": [
-//CHECK-SARIF-NEXT: {
-//CHECK-SARIF-NEXT: "physicalLocation": {
-//CHECK-SARIF-NEXT: "artifactLocation": {
-//CHECK-SARIF-NEXT: "index": 0,
-//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
-//CHECK-SARIF-NEXT: },
-//CHECK-SARIF-NEXT: "region": {
-//CHECK-SARIF-NEXT: "endColumn": 7,
-//CHECK-SARIF-NEXT: "endLine": 3,
-//CHECK-SARIF-NEXT: "startColumn": 4,
-//CHECK-SARIF-NEXT: "startLine": 3
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: ],
-//CHECK-SARIF-NEXT: "message": {
-//CHECK-SARIF-NEXT: "text": "Dereference of null pointer (loaded from variable 'ptr')"
-//CHECK-SARIF-NEXT: },
-//CHECK-SARIF-NEXT: "ruleId": "clang-analyzer-core.NullDereference",
-//CHECK-SARIF-NEXT: "ruleIndex": 0
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: ],
-//CHECK-SARIF-NEXT: "tool": {
-//CHECK-SARIF-NEXT: "driver": {
-//CHECK-SARIF-NEXT: "fullName": "clang-tidy",
-//CHECK-SARIF-NEXT: "informationUri": "https://clang.llvm.org/extra/clang-tidy/",
-//CHECK-SARIF-NEXT: "language": "en-US",
-//CHECK-SARIF-NEXT: "name": "clang-tidy",
-//CHECK-SARIF-NEXT: "rules": [
-//CHECK-SARIF-NEXT: {
-//CHECK-SARIF-NEXT: "defaultConfiguration": {
-//CHECK-SARIF-NEXT: "enabled": true,
-//CHECK-SARIF-NEXT: "level": "warning",
-//CHECK-SARIF-NEXT: "rank": -1
-//CHECK-SARIF-NEXT: },
-//CHECK-SARIF-NEXT: "fullDescription": {
-//CHECK-SARIF-NEXT: "text": ""
-//CHECK-SARIF-NEXT: },
-//CHECK-SARIF-NEXT: "id": "clang-analyzer-core.NullDereference",
-//CHECK-SARIF-NEXT: "name": "clang-analyzer-core.NullDereference"
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: ],
-//CHECK-SARIF-NEXT: "version": "{{.*}}"
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: }
-//CHECK-SARIF-NEXT: ],
-//CHECK-SARIF-NEXT: "version": "{{.*}}"
-//CHECK-SARIF-NEXT: }
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-EOF.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-EOF.cpp
new file mode 100644
index 0000000000000..a0703fc0790ec
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-EOF.cpp
@@ -0,0 +1,78 @@
+// RUN: echo -n 'int main() { return 0; }' > %t-input.cpp
+// RUN: clang-tidy %t-input.cpp -checks='-*,clang-diagnostic-newline-eof' --allow-no-checks -sarif-export=%t.sarif -- -Wnewline-eof > %t.msg 2>&1
+// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MESSAGES %s -implicit-check-not='{{warning|error|note}}:'
+// RUN: FileCheck -input-file=%t.sarif -check-prefix=CHECK-SARIF %s
+
+//CHECK-MESSAGES: -input.cpp:1:25: warning: no newline at end of file [clang-diagnostic-newline-eof]
+
+//CHECK-SARIF: {
+//CHECK-SARIF-NEXT: "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json",
+//CHECK-SARIF-NEXT: "runs": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "artifacts": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "length": {{[0-9]+}},
+//CHECK-SARIF-NEXT: "location": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "mimeType": "text/plain",
+//CHECK-SARIF-NEXT: "roles": [
+//CHECK-SARIF-NEXT: "resultFile"
+//CHECK-SARIF-NEXT: ]
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "columnKind": "unicodeCodePoints",
+//CHECK-SARIF-NEXT: "results": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "level": "warning",
+//CHECK-SARIF-NEXT: "locations": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "physicalLocation": {
+//CHECK-SARIF-NEXT: "artifactLocation": {
+//CHECK-SARIF-NEXT: "index": 0,
+//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "region": {
+//CHECK-SARIF-NEXT: "endColumn": 25,
+//CHECK-SARIF-NEXT: "startColumn": 25,
+//CHECK-SARIF-NEXT: "startLine": 1
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "message": {
+//CHECK-SARIF-NEXT: "text": "no newline at end of file"
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "ruleId": "clang-diagnostic-newline-eof",
+//CHECK-SARIF-NEXT: "ruleIndex": 0
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "tool": {
+//CHECK-SARIF-NEXT: "driver": {
+//CHECK-SARIF-NEXT: "fullName": "clang-tidy",
+//CHECK-SARIF-NEXT: "informationUri": "https://clang.llvm.org/extra/clang-tidy/",
+//CHECK-SARIF-NEXT: "language": "en-US",
+//CHECK-SARIF-NEXT: "name": "clang-tidy",
+//CHECK-SARIF-NEXT: "rules": [
+//CHECK-SARIF-NEXT: {
+//CHECK-SARIF-NEXT: "defaultConfiguration": {
+//CHECK-SARIF-NEXT: "enabled": true,
+//CHECK-SARIF-NEXT: "level": "warning",
+//CHECK-SARIF-NEXT: "rank": -1
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "fullDescription": {
+//CHECK-SARIF-NEXT: "text": ""
+//CHECK-SARIF-NEXT: },
+//CHECK-SARIF-NEXT: "id": "clang-diagnostic-newline-eof",
+//CHECK-SARIF-NEXT: "name": "clang-diagnostic-newline-eof"
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "version": "24.0.0git"
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: }
+//CHECK-SARIF-NEXT: ],
+//CHECK-SARIF-NEXT: "version": "2.1.0"
+
+
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-macro.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-macro-thread-flows.cpp
similarity index 90%
rename from clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-macro.cpp
rename to clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-macro-thread-flows.cpp
index cedbaf62c8858..f6e5d0476926a 100644
--- a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-macro.cpp
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-macro-thread-flows.cpp
@@ -4,16 +4,12 @@
// RUN: FileCheck -input-file=%t.sarif -check-prefix=CHECK-SARIF %s
#define DEREF(p) (*(p))
#define NUL nullptr
+int g() { int *ptr = NUL; return DEREF(ptr); }
-int g() {
- int *ptr = NUL;
- return DEREF(ptr);
-}
-
-//CHECK-MESSAGES: -input.cpp:6:10: warning: Dereference of null pointer (loaded from variable 'ptr') [clang-analyzer-core.NullDereference]
+//CHECK-MESSAGES: -input.cpp:3:34: warning: Dereference of null pointer (loaded from variable 'ptr') [clang-analyzer-core.NullDereference]
//CHECK-MESSAGES: -input.cpp:1:18: note: expanded from macro 'DEREF'
-//CHECK-MESSAGES: -input.cpp:5:3: note: 'ptr' initialized to a null pointer value
-//CHECK-MESSAGES: -input.cpp:6:10: note: Dereference of null pointer (loaded from variable 'ptr')
+//CHECK-MESSAGES: -input.cpp:3:11: note: 'ptr' initialized to a null pointer value
+//CHECK-MESSAGES: -input.cpp:3:34: note: Dereference of null pointer (loaded from variable 'ptr')
//CHECK-MESSAGES: -input.cpp:1:18: note: expanded from macro 'DEREF'
//CHECK-SARIF: {
@@ -73,10 +69,10 @@ int g() {
//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "region": {
-//CHECK-SARIF-NEXT: "endColumn": 11,
-//CHECK-SARIF-NEXT: "endLine": 5,
-//CHECK-SARIF-NEXT: "startColumn": 3,
-//CHECK-SARIF-NEXT: "startLine": 5
+//CHECK-SARIF-NEXT: "endColumn": 19,
+//CHECK-SARIF-NEXT: "endLine": 3,
+//CHECK-SARIF-NEXT: "startColumn": 11,
+//CHECK-SARIF-NEXT: "startLine": 3
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: }
@@ -93,10 +89,10 @@ int g() {
//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "region": {
-//CHECK-SARIF-NEXT: "endColumn": 19,
-//CHECK-SARIF-NEXT: "endLine": 6,
-//CHECK-SARIF-NEXT: "startColumn": 16,
-//CHECK-SARIF-NEXT: "startLine": 6
+//CHECK-SARIF-NEXT: "endColumn": 43,
+//CHECK-SARIF-NEXT: "endLine": 3,
+//CHECK-SARIF-NEXT: "startColumn": 40,
+//CHECK-SARIF-NEXT: "startLine": 3
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: }
@@ -135,10 +131,10 @@ int g() {
//CHECK-SARIF-NEXT: "uri": "file://{{.*}}-input.cpp"
//CHECK-SARIF-NEXT: },
//CHECK-SARIF-NEXT: "region": {
-//CHECK-SARIF-NEXT: "endColumn": 19,
-//CHECK-SARIF-NEXT: "endLine": 6,
-//CHECK-SARIF-NEXT: "startColumn": 16,
-//CHECK-SARIF-NEXT: "startLine": 6
+//CHECK-SARIF-NEXT: "endColumn": 43,
+//CHECK-SARIF-NEXT: "endLine": 3,
+//CHECK-SARIF-NEXT: "startColumn": 40,
+//CHECK-SARIF-NEXT: "startLine": 3
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: }
>From 98bfad2c09c82d9b8aeb8dd9b739ce16f4e067fb Mon Sep 17 00:00:00 2001
From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)"
<jones.matthew at siemens.com>
Date: Thu, 1 Oct 2026 02:08:39 -0700
Subject: [PATCH 12/12] [clang-tidy] fixed test versions
---
.../infrastructure/export-SARIF-diagnostics-EOF.cpp | 4 ++--
.../export-SARIF-diagnostics-macro-thread-flows.cpp | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-EOF.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-EOF.cpp
index a0703fc0790ec..3ceb3bb8aac16 100644
--- a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-EOF.cpp
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-EOF.cpp
@@ -68,11 +68,11 @@
//CHECK-SARIF-NEXT: "name": "clang-diagnostic-newline-eof"
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: ],
-//CHECK-SARIF-NEXT: "version": "24.0.0git"
+//CHECK-SARIF-NEXT: "version": "{{.*}}"
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: ],
-//CHECK-SARIF-NEXT: "version": "2.1.0"
+//CHECK-SARIF-NEXT: "version": "{{.*}}"
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-macro-thread-flows.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-macro-thread-flows.cpp
index f6e5d0476926a..5f8917f119ecb 100644
--- a/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-macro-thread-flows.cpp
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/export-SARIF-diagnostics-macro-thread-flows.cpp
@@ -166,10 +166,10 @@ int g() { int *ptr = NUL; return DEREF(ptr); }
//CHECK-SARIF-NEXT: "name": "clang-analyzer-core.NullDereference"
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: ],
-//CHECK-SARIF-NEXT: "version": "24.0.0git"
+//CHECK-SARIF-NEXT: "version": "{{.*}}"
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: }
//CHECK-SARIF-NEXT: ],
-//CHECK-SARIF-NEXT: "version": "2.1.0"
+//CHECK-SARIF-NEXT: "version": "{{.*}}"
//CHECK-SARIF-NEXT: }
\ No newline at end of file
More information about the cfe-commits
mailing list