[clang] [clang] [diagnostics] Add `-fdiagnostics-add-output` switch for SARIF (PR #185201)

via cfe-commits cfe-commits at lists.llvm.org
Sat Mar 7 08:27:16 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Dave Bartolomeo (dbartol)

<details>
<summary>Changes</summary>

This change introduces the new `-fdiagnostics-add-output=` switch, which adds an additional output file for diagnostics. The syntax of the switch, which matches an existing GCC switch, is `-fdiagnostics-add-output=format:key1=value1,key2=value2...`. `format` specifies the format of the diagnostics; currently, only `sarif` is supported, but it would be easy enough to add other formats. The optional key-value pairs specify options specific to that format. For `sarif`, the valid options are:

- `file=<path>` - Specifies the path to the generated SARIF file. Unlike GCC, this property is required. We could make it optional by providing a default based on the input filename, but plumbing that through would have made the implementation more complex.
- `version=<version>` - Specifies the SARIF schema version. Only `2.1` is currently supported.

The `-fdiagnostics-add-output=` switch can be specified multiple times. Each occurrence adds an additional output sink, so multiple SARIF logs are possible with different file paths.
- I've added a positive test for the new switch, including specifying it multiple times.
- I've added a negative test for various malformed format/key/value strings.
- I've updated our existing SARIF test to use the same normalize_sarif filter that the Static Analyzer's SARIF tests use, rather than a complicated set of CHECK directives.

rdar://160939516

---

Patch is 23.98 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/185201.diff


15 Files Affected:

- (modified) clang/include/clang/Basic/DiagnosticDriverKinds.td (+14) 
- (modified) clang/include/clang/Basic/DiagnosticOptions.h (+3) 
- (modified) clang/include/clang/Basic/Sarif.h (+17-8) 
- (modified) clang/include/clang/Frontend/SARIFDiagnostic.h (+2-3) 
- (modified) clang/include/clang/Frontend/SARIFDiagnosticPrinter.h (+7-2) 
- (modified) clang/include/clang/Options/Options.td (+6) 
- (modified) clang/lib/Basic/Sarif.cpp (+21-5) 
- (modified) clang/lib/Driver/ToolChains/Clang.cpp (+6) 
- (modified) clang/lib/Frontend/CompilerInstance.cpp (+50-1) 
- (modified) clang/lib/Frontend/FrontendAction.cpp (-5) 
- (modified) clang/lib/Frontend/SARIFDiagnostic.cpp (+1-1) 
- (modified) clang/lib/Frontend/SARIFDiagnosticPrinter.cpp (+85-6) 
- (added) clang/test/Frontend/Inputs/expected-sarif/sarif-log.cpp.sarif (+105) 
- (added) clang/test/Frontend/sarif-log-invalid.cpp (+9) 
- (added) clang/test/Frontend/sarif-log.cpp (+12) 


``````````diff
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 972cc87464769..745d7e2e07be8 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -586,6 +586,20 @@ def err_analyzer_not_built_with_z3 : Error<
   "analyzer constraint manager 'z3' is only available if LLVM was built with "
   "-DLLVM_ENABLE_Z3_SOLVER=ON">;
 
+def err_diagnostic_output_no_value
+    : Error<"'-fdiagnostics-add-output' key '%0' has no value">;
+def err_diagnostic_output_unknown_format
+    : Error<"'-fdiagnostics-add-output' uses unrecognized format '%0'">;
+def err_diagnostic_output_unknown_key
+    : Error<"'-fdiagnostics-add-output' with format '%0' uses unrecognized key "
+            "'%1'">;
+def err_invalid_sarif_version
+    : Error<"'-fdiagnostics-add-output' specifies unsupported SARIF version "
+            "'%0'. Supported versions are: %1">;
+def err_missing_sarif_file_name
+    : Error<"'-fdiagnostics-add-output' with format 'sarif' is missing "
+            "required key 'file'">;
+
 def warn_drv_needs_hvx : Warning<
   "%0 requires HVX, use -mhvx/-mhvx= to enable it">,
   InGroup<OptionIgnored>;
diff --git a/clang/include/clang/Basic/DiagnosticOptions.h b/clang/include/clang/Basic/DiagnosticOptions.h
index a230022224de5..47f3135f9237a 100644
--- a/clang/include/clang/Basic/DiagnosticOptions.h
+++ b/clang/include/clang/Basic/DiagnosticOptions.h
@@ -107,6 +107,9 @@ class DiagnosticOptions {
   /// The file to serialize diagnostics to (non-appending).
   std::string DiagnosticSerializationFile;
 
+  /// List of arguments to `-fdiagnostics-add-output=`
+  std::vector<std::string> AdditionalOutputConfigs;
+
   /// Path for the file that defines diagnostic suppression mappings.
   std::string DiagnosticSuppressionMappingsFile;
 
diff --git a/clang/include/clang/Basic/Sarif.h b/clang/include/clang/Basic/Sarif.h
index 7651d2ac7a768..c7610794e2163 100644
--- a/clang/include/clang/Basic/Sarif.h
+++ b/clang/include/clang/Basic/Sarif.h
@@ -50,6 +50,13 @@ namespace clang {
 class SarifDocumentWriter;
 class SourceManager;
 
+struct SarifVersion {
+  StringRef SchemaVersion;
+  StringRef CommandLineVersion;
+  StringRef SchemaURI;
+  bool IsDefault;
+};
+
 namespace detail {
 
 /// \internal
@@ -413,10 +420,7 @@ class SarifResult {
 ///    aggregation methods such as SarifDocumentWriter::appendResult etc.
 class SarifDocumentWriter {
 private:
-  const llvm::StringRef SchemaURI{
-      "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/"
-      "sarif-schema-2.1.0.json"};
-  const llvm::StringRef SchemaVersion{"2.1.0"};
+  const SarifVersion Version;
 
   /// \internal
   /// Return a pointer to the current tool. Asserts that a run exists.
@@ -460,16 +464,17 @@ class SarifDocumentWriter {
   SarifDocumentWriter() = delete;
 
   /// Create a new empty SARIF document with the given source manager.
-  SarifDocumentWriter(const SourceManager &SourceMgr) : SourceMgr(SourceMgr) {}
+  SarifDocumentWriter(const SourceManager &SourceMgr,
+                      const SarifVersion &Version = getDefaultVersion())
+      : Version(Version), SourceMgr(SourceMgr) {}
 
   /// Release resources held by this SARIF document.
   ~SarifDocumentWriter() = default;
 
   /// Create a new run with which any upcoming analysis will be associated.
   /// Each run requires specifying the tool that is generating reporting items.
-  void createRun(const llvm::StringRef ShortToolName,
-                 const llvm::StringRef LongToolName,
-                 const llvm::StringRef ToolVersion = CLANG_VERSION_STRING);
+  void createRun(std::string ShortToolName, std::string LongToolName,
+                 std::string ToolVersion = CLANG_VERSION_STRING);
 
   /// If there is a current run, end it.
   ///
@@ -511,6 +516,10 @@ class SarifDocumentWriter {
 
   static std::string fileNameToURI(llvm::StringRef Filename);
 
+  static ArrayRef<SarifVersion> getSupportedVersions();
+
+  static const SarifVersion &getDefaultVersion();
+
 private:
   /// Source Manager to use for the current SARIF document.
   const SourceManager &SourceMgr;
diff --git a/clang/include/clang/Frontend/SARIFDiagnostic.h b/clang/include/clang/Frontend/SARIFDiagnostic.h
index 7a6f27eb3b9fa..f2933ec832f43 100644
--- a/clang/include/clang/Frontend/SARIFDiagnostic.h
+++ b/clang/include/clang/Frontend/SARIFDiagnostic.h
@@ -22,9 +22,8 @@ namespace clang {
 
 class SARIFDiagnostic : public DiagnosticRenderer {
 public:
-  SARIFDiagnostic(raw_ostream &OS, const LangOptions &LangOpts,
-                  DiagnosticOptions &DiagOpts, SarifDocumentWriter *Writer);
-
+  SARIFDiagnostic(const LangOptions &LangOpts, DiagnosticOptions &DiagOpts,
+                  SarifDocumentWriter *Writer);
   ~SARIFDiagnostic() = default;
 
   SARIFDiagnostic &operator=(const SARIFDiagnostic &&) = delete;
diff --git a/clang/include/clang/Frontend/SARIFDiagnosticPrinter.h b/clang/include/clang/Frontend/SARIFDiagnosticPrinter.h
index 3406ed16c2fba..0877867757bbe 100644
--- a/clang/include/clang/Frontend/SARIFDiagnosticPrinter.h
+++ b/clang/include/clang/Frontend/SARIFDiagnosticPrinter.h
@@ -29,7 +29,7 @@ class SarifDocumentWriter;
 
 class SARIFDiagnosticPrinter : public DiagnosticConsumer {
 public:
-  SARIFDiagnosticPrinter(raw_ostream &OS, DiagnosticOptions &DiagOpts);
+  SARIFDiagnosticPrinter(llvm::StringRef FilePath, DiagnosticOptions &DiagOpts);
   ~SARIFDiagnosticPrinter() = default;
 
   SARIFDiagnosticPrinter &operator=(const SARIFDiagnosticPrinter &&) = delete;
@@ -58,8 +58,13 @@ class SARIFDiagnosticPrinter : public DiagnosticConsumer {
   void HandleDiagnostic(DiagnosticsEngine::Level Level,
                         const Diagnostic &Info) override;
 
+  static std::unique_ptr<SARIFDiagnosticPrinter>
+  create(ArrayRef<std::pair<StringRef, StringRef>> Config,
+         DiagnosticOptions &DiagOpts, DiagnosticsEngine &Diags);
+
 private:
-  raw_ostream &OS;
+  std::string FilePath;
+
   DiagnosticOptions &DiagOpts;
 
   /// Handle to the currently active SARIF diagnostic emitter.
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index cc05fb71c84e4..9927782e2768a 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -6729,6 +6729,12 @@ def _profile : Flag<["--"], "profile">, Alias<p>;
 def _resource_EQ : Joined<["--"], "resource=">, Alias<fcompile_resource_EQ>;
 def _resource : Separate<["--"], "resource">, Alias<fcompile_resource_EQ>;
 def _rtlib : Separate<["--"], "rtlib">, Alias<rtlib_EQ>;
+def fdiagnostics_add_output_EQ
+    : Joined<["-"], "fdiagnostics-add-output=">,
+      Group<f_clang_Group>,
+      Visibility<[ClangOption, CC1Option]>,
+      HelpText<"Generate additional diagnostics log file">,
+      MarshallingInfoStringVector<DiagnosticOpts<"AdditionalOutputConfigs">>;
 def _serialize_diags : Separate<["-", "--"], "serialize-diagnostics">,
   Flags<[NoXarchOption]>,
   HelpText<"Serialize compiler diagnostics to a file">;
diff --git a/clang/lib/Basic/Sarif.cpp b/clang/lib/Basic/Sarif.cpp
index e829b9bb47ad9..fd1800bdbf30a 100644
--- a/clang/lib/Basic/Sarif.cpp
+++ b/clang/lib/Basic/Sarif.cpp
@@ -342,9 +342,9 @@ SarifDocumentWriter::createCodeFlow(ArrayRef<ThreadFlow> ThreadFlows) {
   return json::Object{{"threadFlows", createThreadFlows(ThreadFlows)}};
 }
 
-void SarifDocumentWriter::createRun(StringRef ShortToolName,
-                                    StringRef LongToolName,
-                                    StringRef ToolVersion) {
+void SarifDocumentWriter::createRun(std::string ShortToolName,
+                                    std::string LongToolName,
+                                    std::string ToolVersion) {
   // Clear resources associated with a previous run.
   endRun();
 
@@ -439,10 +439,26 @@ json::Object SarifDocumentWriter::createDocument() {
   endRun();
 
   json::Object Doc{
-      {"$schema", SchemaURI},
-      {"version", SchemaVersion},
+      {"$schema", Version.SchemaURI},
+      {"version", Version.SchemaVersion},
   };
   if (!Runs.empty())
     Doc["runs"] = json::Array(Runs);
   return Doc;
 }
+
+ArrayRef<SarifVersion> SarifDocumentWriter::getSupportedVersions() {
+  static const SarifVersion Versions[] = {
+      {"2.1.0", "2.1",
+       "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/"
+       "sarif-schema-2.1.0.json",
+       true}};
+
+  return Versions;
+}
+
+const SarifVersion &SarifDocumentWriter::getDefaultVersion() {
+  const auto Versions = getSupportedVersions();
+  return *std::find_if(Versions.begin(), Versions.end(),
+                       [](const SarifVersion &V) { return V.IsDefault; });
+}
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 36ba3d35ed012..9f4b077a62132 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7700,6 +7700,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
     CmdArgs.push_back(Args.MakeArgString(A->getValue()));
   }
 
+  // Create additional diagnostic logs
+  for (Arg *A : Args.filtered(options::OPT_fdiagnostics_add_output_EQ)) {
+    A->render(Args, CmdArgs);
+    A->claim();
+  }
+
   if (Args.hasArg(options::OPT_fretain_comments_from_system_headers))
     CmdArgs.push_back("-fretain-comments-from-system-headers");
 
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 60914d9b2cbc7..98bd89c507e2e 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -12,6 +12,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/Basic/CharInfo.h"
 #include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticDriver.h"
 #include "clang/Basic/DiagnosticFrontend.h"
 #include "clang/Basic/DiagnosticOptions.h"
 #include "clang/Basic/FileManager.h"
@@ -26,6 +27,7 @@
 #include "clang/Frontend/FrontendActions.h"
 #include "clang/Frontend/FrontendPluginRegistry.h"
 #include "clang/Frontend/LogDiagnosticPrinter.h"
+#include "clang/Frontend/SARIFDiagnostic.h"
 #include "clang/Frontend/SARIFDiagnosticPrinter.h"
 #include "clang/Frontend/SerializedDiagnosticPrinter.h"
 #include "clang/Frontend/TextDiagnosticPrinter.h"
@@ -342,6 +344,47 @@ static void SetupSerializedDiagnostics(DiagnosticOptions &DiagOpts,
   }
 }
 
+static void SetupAdditionalLogs(DiagnosticOptions &DiagOpts,
+                                DiagnosticsEngine &Diags) {
+  for (StringRef Config : DiagOpts.AdditionalOutputConfigs) {
+    auto [Format, PairsString] = Config.split(":");
+
+    SmallVector<StringRef, 4> PairStrings;
+    PairsString.split(PairStrings, ",", -1, /* KeepEmpty = */ false);
+    SmallVector<std::pair<StringRef, StringRef>, 4> Pairs;
+    for (const auto &PairString : PairStrings) {
+      std::pair<StringRef, StringRef> Pair = PairString.split("=");
+      if (Pair.second.empty()) {
+        Diags.Report(SourceLocation(), diag::err_diagnostic_output_no_value)
+            << Pair.first;
+        break;
+      }
+
+      Pairs.push_back(PairString.split("="));
+    }
+
+    std::unique_ptr<DiagnosticConsumer> Consumer;
+    if (Format == "sarif") {
+      Consumer = SARIFDiagnosticPrinter::create(Pairs, DiagOpts, Diags);
+
+    } else {
+      // Only support "sarif" for now.
+      Diags.Report(SourceLocation(), diag::err_diagnostic_output_unknown_format)
+          << Format;
+    }
+
+    if (Consumer) {
+      if (Diags.ownsClient()) {
+        Diags.setClient(new ChainedDiagnosticConsumer(Diags.takeClient(),
+                                                      std::move(Consumer)));
+      } else {
+        Diags.setClient(new ChainedDiagnosticConsumer(Diags.getClient(),
+                                                      std::move(Consumer)));
+      }
+    }
+  }
+}
+
 void CompilerInstance::createDiagnostics(DiagnosticConsumer *Client,
                                          bool ShouldOwnClient) {
   Diagnostics = createDiagnostics(getVirtualFileSystem(), getDiagnosticOpts(),
@@ -360,7 +403,9 @@ IntrusiveRefCntPtr<DiagnosticsEngine> CompilerInstance::createDiagnostics(
   if (Client) {
     Diags->setClient(Client, ShouldOwnClient);
   } else if (Opts.getFormat() == DiagnosticOptions::SARIF) {
-    Diags->setClient(new SARIFDiagnosticPrinter(llvm::errs(), Opts));
+    /* TO_UPSTREAM(SARIF) ON */
+    Diags->setClient(new SARIFDiagnosticPrinter("", Opts));
+    /* TO_UPSTREAM(SARIF) OFF */
   } else
     Diags->setClient(new TextDiagnosticPrinter(llvm::errs(), Opts));
 
@@ -375,6 +420,10 @@ IntrusiveRefCntPtr<DiagnosticsEngine> CompilerInstance::createDiagnostics(
   if (!Opts.DiagnosticSerializationFile.empty())
     SetupSerializedDiagnostics(Opts, *Diags, Opts.DiagnosticSerializationFile);
 
+  /* TO_UPSTREAM(SARIF) ON */
+  SetupAdditionalLogs(Opts, *Diags);
+  /* TO_UPSTREAM(SARIF) OFF */
+
   // Configure our handling of diagnostics.
   ProcessWarningOptions(*Diags, Opts, VFS);
 
diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index 73f092521546f..78fe306d1579e 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -977,11 +977,6 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
     CI.createFileManager();
   if (!CI.hasSourceManager()) {
     CI.createSourceManager();
-    if (CI.getDiagnosticOpts().getFormat() == DiagnosticOptions::SARIF) {
-      static_cast<SARIFDiagnosticPrinter *>(&CI.getDiagnosticClient())
-          ->setSarifWriter(
-              std::make_unique<SarifDocumentWriter>(CI.getSourceManager()));
-    }
   }
 
   // Set up embedding for any specified files. Do this before we load any
diff --git a/clang/lib/Frontend/SARIFDiagnostic.cpp b/clang/lib/Frontend/SARIFDiagnostic.cpp
index 04ee14edfe74c..c96dc03552f31 100644
--- a/clang/lib/Frontend/SARIFDiagnostic.cpp
+++ b/clang/lib/Frontend/SARIFDiagnostic.cpp
@@ -30,7 +30,7 @@
 
 namespace clang {
 
-SARIFDiagnostic::SARIFDiagnostic(raw_ostream &OS, const LangOptions &LangOpts,
+SARIFDiagnostic::SARIFDiagnostic(const LangOptions &LangOpts,
                                  DiagnosticOptions &DiagOpts,
                                  SarifDocumentWriter *Writer)
     : DiagnosticRenderer(LangOpts, DiagOpts), Writer(Writer) {}
diff --git a/clang/lib/Frontend/SARIFDiagnosticPrinter.cpp b/clang/lib/Frontend/SARIFDiagnosticPrinter.cpp
index 72b796f8db798..d05bd33efbcc2 100644
--- a/clang/lib/Frontend/SARIFDiagnosticPrinter.cpp
+++ b/clang/lib/Frontend/SARIFDiagnosticPrinter.cpp
@@ -11,37 +11,116 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Frontend/SARIFDiagnosticPrinter.h"
+#include "clang/Basic/DiagnosticDriver.h"
 #include "clang/Basic/DiagnosticOptions.h"
 #include "clang/Basic/Sarif.h"
 #include "clang/Frontend/DiagnosticRenderer.h"
 #include "clang/Frontend/SARIFDiagnostic.h"
 #include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/JSON.h"
 #include "llvm/Support/raw_ostream.h"
 
 namespace clang {
 
-SARIFDiagnosticPrinter::SARIFDiagnosticPrinter(raw_ostream &OS,
+SARIFDiagnosticPrinter::SARIFDiagnosticPrinter(llvm::StringRef FilePath,
                                                DiagnosticOptions &DiagOpts)
-    : OS(OS), DiagOpts(DiagOpts) {}
+    : FilePath(FilePath), DiagOpts(DiagOpts) {}
+
+std::unique_ptr<SARIFDiagnosticPrinter>
+SARIFDiagnosticPrinter::create(ArrayRef<std::pair<StringRef, StringRef>> Config,
+                               DiagnosticOptions &DiagOpts,
+                               DiagnosticsEngine &Diags) {
+  std::optional<std::string> FilePath;
+  SarifVersion Version = SarifDocumentWriter::getDefaultVersion();
+
+  for (const auto &Pair : Config) {
+    if (Pair.first == "file") {
+      FilePath = Pair.second;
+    } else if (Pair.first == "version") {
+      auto SupportedVersions = SarifDocumentWriter::getSupportedVersions();
+      auto FoundVersion =
+          std::find_if(SupportedVersions.begin(), SupportedVersions.end(),
+                       [=](const SarifVersion &V) {
+                         return V.CommandLineVersion == Pair.second;
+                       });
+      if (FoundVersion != SupportedVersions.end()) {
+        Version = *FoundVersion;
+      } else {
+        SmallString<64> SupportedList;
+        bool First = true;
+        for (const auto &V : SupportedVersions) {
+          if (First) {
+            First = false;
+          } else {
+            SupportedList.append(", ");
+          }
+          SupportedList.append("'");
+          SupportedList.append(V.CommandLineVersion);
+          SupportedList.append("'");
+        }
+        Diags.Report(SourceLocation(), diag::err_invalid_sarif_version)
+            << Pair.second << SupportedList;
+      }
+    } else {
+      Diags.Report(SourceLocation(), diag::err_diagnostic_output_unknown_key)
+          << "sarif" << Pair.second;
+    }
+  }
+
+  if (!FilePath) {
+    // We should probably have a default here based on the input file name or
+    // the output object file name, but I'm not sure how to get that information
+    // here.
+    Diags.Report(SourceLocation(), diag::err_missing_sarif_file_name);
+    return {};
+  }
+
+  return std::make_unique<SARIFDiagnosticPrinter>(*FilePath, DiagOpts);
+}
 
 void SARIFDiagnosticPrinter::BeginSourceFile(const LangOptions &LO,
                                              const Preprocessor *PP) {
   // Build the SARIFDiagnostic utility.
+  if (!hasSarifWriter() && PP) {
+    // Use the SourceManager from the preprocessor.
+    // REVIEW: Are there cases where we won't have a preprocessor but we will
+    // have a SourceManager? If so, we should pass the SourceManager directly to
+    // the BeginSourceFile call.
+    setSarifWriter(
+        std::make_unique<SarifDocumentWriter>(PP->getSourceManager()));
+  }
   assert(hasSarifWriter() && "Writer not set!");
   assert(!SARIFDiag && "SARIFDiagnostic already set.");
-  SARIFDiag = std::make_unique<SARIFDiagnostic>(OS, LO, DiagOpts, &*Writer);
+  SARIFDiag = std::make_unique<SARIFDiagnostic>(LO, DiagOpts, &*Writer);
   // Initialize the SARIF object.
-  Writer->createRun("clang", Prefix);
+  Writer->createRun("clang", Prefix, getClangFullVersion());
 }
 
 void SARIFDiagnosticPrinter::EndSourceFile() {
   assert(SARIFDiag && "SARIFDiagnostic has not been set.");
   Writer->endRun();
   llvm::json::Value Value(Writer->createDocument());
-  OS << llvm::formatv("\n{0:2}\n\n", Value);
-  OS.flush();
+  if (FilePath.empty()) {
+    // Write to console.
+    llvm::errs() << llvm::formatv("\n{0:2}\n\n", Value);
+    llvm::errs().flush();
+  } else {
+    // Write to file.
+    std::error_code EC;
+    llvm::raw_fd_ostream OS(FilePath, EC, llvm::sys::fs::OF_TextWithCRLF);
+    if (EC) {
+      // FIXME: Emit a real diagnostic, similar to how the serialized diagnostic
+      // log does via getMetaDiags().
+      llvm::errs() << "warning: could not create file: " << EC.message()
+                   << '\n';
+    } else {
+      OS << llvm::formatv("{0:2}\n", Value);
+    }
+  }
+
   SARIFDiag.reset();
 }
 
diff --git a/clang/test/Frontend/Inputs/expected-sarif/sarif-log.cpp.sarif b/clang/test/Frontend/Inputs/expected-sarif/sarif-log.cpp.sarif
new file mode 100644
index 0000000000000..811d70a3eeed0
--- /dev/null
+++ b/clang/test/Frontend/Inputs/expected-sarif/sarif-log.cpp.sarif
@@ -0,0 +1,105 @@
+{
+  "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json",
+  "runs": [
+    {
+      "artifacts": [
+        {
+          "length": -1,
+          "location": {
+            "index": 0,
+            "uri": "file:///[...]/sarif-log.cpp"
+          },
+          "mimeType": "text/plain",
+          "roles": [
+            "resultFile"
+          ]
+        }
+      ],
+      "columnKind": "unicodeCodePoints",
+      "results": [
+        {
+          "level": "warning",
+          "locations": [
+            {
+              "physicalLocation": {
+                "artifactLocation": {
+                  "index": 0,
+                  "uri": "file:///[...]/sarif-log.cpp"
+                },
+                "region": {
+                  "endColumn": 3,
+                  "startColumn": 3,
+  ...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/185201


More information about the cfe-commits mailing list