https://github.com/matthew-j-code updated https://github.com/llvm/llvm-project/pull/219182
>From 57bef2a697e270f4c58714f87dd84ba562ba05b1 Mon Sep 17 00:00:00 2001 From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)" <[email protected]> Date: Fri, 10 Jul 2026 07:11:06 -0700 Subject: [PATCH 1/6] [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 6fe497e5f6eaf..8c689dc337441 100644 --- a/clang-tools-extra/docs/ReleaseNotes.md +++ b/clang-tools-extra/docs/ReleaseNotes.md @@ -100,6 +100,10 @@ infrastructure are described first, followed by tool-specific sections. `-std=cXX-or-earlier` values, mirroring the existing `-std=cXX-or-later`. New construct expands to the given standard and every earlier one. +- 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 10ff2cbafd31c737660d52f8db32dca5b5e0ed67 Mon Sep 17 00:00:00 2001 From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)" <[email protected]> Date: Fri, 28 Aug 2026 04:09:07 -0700 Subject: [PATCH 2/6] 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 1cc22af9be6732100a096427b9349964caad7947 Mon Sep 17 00:00:00 2001 From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)" <[email protected]> Date: Fri, 28 Aug 2026 06:08:54 -0700 Subject: [PATCH 3/6] 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 34d46219c4034c606766d3352a34fbc55486264b Mon Sep 17 00:00:00 2001 From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)" <[email protected]> Date: Fri, 28 Aug 2026 06:24:22 -0700 Subject: [PATCH 4/6] 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 41d84ca5cc723d34240da2e260b394d064f5eaa9 Mon Sep 17 00:00:00 2001 From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)" <[email protected]> Date: Tue, 1 Sep 2026 06:24:17 -0700 Subject: [PATCH 5/6] 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 2a87b7fbde809432c3bdb1adcc7f102dd4d1565e Mon Sep 17 00:00:00 2001 From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)" <[email protected]> Date: Tue, 1 Sep 2026 08:00:57 -0700 Subject: [PATCH 6/6] 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", _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
