cjdb created this revision.
Herald added a project: All.
cjdb requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

This is one of two alternative designs that Clang can adopt when writing
SARIF to file. In this design, the Clang driver invokes a separate tool
that "links" the independent diagnostic files into a single one called
`<target>_sarif`.

Please discuss the //design// of this patch over at
https://discourse.llvm.org/t/adding-options-for-clang-to-write-diagnostics-to-file/67762/,
alongside <OTHER CL>, so that we can incorporate one of them into D145284 
<https://reviews.llvm.org/D145284>.
I ask that commentary regarding the implementation be held off until
either this patch or <OTHER CL> is integrated into D145284 
<https://reviews.llvm.org/D145284>.

Depends on D145284 <https://reviews.llvm.org/D145284>.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D145438

Files:
  clang/lib/Tooling/CMakeLists.txt
  clang/lib/Tooling/SarifLinker.cpp
  clang/test/Frontend/sarif-diagnostics.cpp
  clang/tools/CMakeLists.txt
  clang/tools/driver/CMakeLists.txt
  clang/tools/driver/driver.cpp
  clang/tools/sarif-ld/CMakeLists.txt
  clang/tools/sarif-ld/SarifLinker.cpp

Index: clang/tools/sarif-ld/SarifLinker.cpp
===================================================================
--- /dev/null
+++ clang/tools/sarif-ld/SarifLinker.cpp
@@ -0,0 +1,92 @@
+#include "clang/Basic/Sarif.h"
+#include "clang/Config/config.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/Process.h"
+#include <cassert>
+#include <cstdlib>
+#include <fstream>
+#include <iterator>
+#include <string>
+#include <string_view>
+#include <system_error>
+
+namespace json = llvm::json;
+namespace cl = llvm::cl;
+using llvm::ArrayRef, llvm::StringRef;
+
+static cl::opt<bool> Help("h", cl::desc("Alias for --help"), cl::Hidden);
+static cl::opt<std::string> Target("o", cl::init("<file>"),
+                                   cl::desc("Write output to <file>.sarif"));
+static cl::list<std::string> Files(cl::Positional, "files",
+                                   cl::desc("[<file> ...]"));
+
+static void PrintVersion(llvm::raw_ostream &OS) {
+  OS << clang::getClangToolFullVersion("sarif-ld") << '\n';
+}
+
+namespace {
+struct ParseArgsResult {
+  llvm::opt::ArgStringList Input;
+  std::string Output;
+};
+} // namespace
+
+static ParseArgsResult ParseArgs(int argc, char *argv[]) {
+  llvm::cl::ParseCommandLineOptions(
+      argc, argv,
+      "A tool that links separate SARIF files into a single SARIF file.");
+
+  if (Help) {
+    cl::PrintHelpMessage();
+    std::exit(0);
+  }
+
+  if (Files.empty()) {
+    llvm::errs() << argv[0] << ": error: no input files\n";
+    cl::PrintHelpMessage();
+    std::exit(1);
+  }
+
+  if (Target.empty()) {
+    llvm::errs() << argv[0] << ": error: no outpt file\n";
+    cl::PrintHelpMessage();
+    std::exit(2);
+  }
+
+  ParseArgsResult Result{llvm::opt::ArgStringList(), Target.getValue()};
+  llvm::transform(Files, std::back_inserter(Result.Input),
+                  [](StringRef File) { return File.data(); });
+  return Result;
+}
+
+namespace llvm::sarif_linker {
+[[noreturn]] void ReportErrorAndExit(llvm::errc error_code, StringRef Message);
+std::error_code LinkSarifDiagnostics(const opt::ArgStringList &InputPaths,
+                                     StringRef OutputPath);
+} // namespace llvm::sarif_linker
+
+int main(int argc, char *argv[]) {
+  llvm::InitLLVM X(argc, argv);
+  llvm::setBugReportMsg("PLEASE submit a bug report to " BUG_REPORT_URL
+                        " and include the crash backtrace, preprocessed "
+                        "source, and associated run script.\n");
+
+  if (llvm::sys::Process::FixupStandardFileDescriptors())
+    return 1;
+
+  llvm::cl::SetVersionPrinter(PrintVersion);
+  auto [Input, Output] = ParseArgs(argc, argv);
+
+  std::error_code EC = llvm::sarif_linker::LinkSarifDiagnostics(Input, Output);
+  if (EC)
+    llvm::sarif_linker::ReportErrorAndExit(static_cast<llvm::errc>(EC.value()),
+                                           EC.message());
+}
Index: clang/tools/sarif-ld/CMakeLists.txt
===================================================================
--- /dev/null
+++ clang/tools/sarif-ld/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_tool(
+  sarif-ld
+  SarifLinker.cpp
+)
+
+clang_target_link_libraries(
+  sarif-ld
+  PRIVATE clangTooling
+)
+
+install(
+  PROGRAMS sarif-ld
+  DESTINATION "${CMAKE_INSTALL_BINDIR}"
+  COMPONENT sarif-ld
+)
Index: clang/tools/driver/driver.cpp
===================================================================
--- clang/tools/driver/driver.cpp
+++ clang/tools/driver/driver.cpp
@@ -372,6 +372,11 @@
   return 1;
 }
 
+namespace llvm::sarif_linker {
+  [[noreturn]] void ReportErrorAndExit(llvm::errc error_code, StringRef Message);
+  std::error_code LinkSarifDiagnostics(const opt::ArgStringList& InputPaths, StringRef OutputPath);
+}
+
 int clang_main(int Argc, char **Argv, const llvm::ToolContext &ToolContext) {
   noteBottomOfStack();
   llvm::InitLLVM X(Argc, Argv);
@@ -634,6 +639,14 @@
     Res = 1;
 #endif
 
+  // Finally, we need to move any SARIF diagnostics into the build directory.
+  const DerivedArgList& ArgList = C->getArgs();
+  if (Arg* DF = ArgList.getLastArg(options::OPT_fdiagnostics_format_EQ); DF && DF->containsValue("sarif-file") && !C->getTempFiles().empty()) {
+    Arg* o = ArgList.getLastArg(options::OPT_o);
+    const char* Out = o ? o->getValue(0) : TheDriver.getDefaultImageName();
+    if (auto EC = llvm::sarif_linker::LinkSarifDiagnostics(C->getTempFiles(), Out)) {}
+  }
+
   // If we have multiple failing commands, we return the result of the first
   // failing command.
   return Res;
Index: clang/tools/driver/CMakeLists.txt
===================================================================
--- clang/tools/driver/CMakeLists.txt
+++ clang/tools/driver/CMakeLists.txt
@@ -43,6 +43,7 @@
   clangFrontend
   clangFrontendTool
   clangSerialization
+  clangTooling
   )
 
 if(WIN32 AND NOT CYGWIN)
Index: clang/tools/CMakeLists.txt
===================================================================
--- clang/tools/CMakeLists.txt
+++ clang/tools/CMakeLists.txt
@@ -51,3 +51,4 @@
 
 add_clang_subdirectory(amdgpu-arch)
 add_clang_subdirectory(nvptx-arch)
+add_clang_subdirectory(sarif-ld)
Index: clang/test/Frontend/sarif-diagnostics.cpp
===================================================================
--- clang/test/Frontend/sarif-diagnostics.cpp
+++ clang/test/Frontend/sarif-diagnostics.cpp
@@ -29,9 +29,26 @@
 
 #include "sarif-diagnostics.hpp"
 
-// RUN: %clang -fsyntax-only -Wall -Wextra -fdiagnostics-format=sarif %s > %t.txt 2>&1 || true
+// RUN: %clang -fsyntax-only -Wall -Wextra -fdiagnostics-format=sarif-stderr %s > %t.txt 2>&1 || true
 // RUN: FileCheck -dump-input=always %s --input-file=%t.txt --check-prefixes=STDERR,SARIF
 
+// RUN: %clang -fsyntax-only -Wall -Wextra -fdiagnostics-format=sarif-file %s > %t.txt 2>&1 || true
+// RUN: FileCheck -dump-input=always --check-prefix=STDERR %s --input-file=%t.txt
+// RUN: FileCheck -dump-input=always --check-prefix=SARIF  %s --input-file=sarif-diagnostics.cpp.sarif
+
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: %clang -Wall -Wextra -fdiagnostics-format=sarif-file %s -o %t/sarif-output > %t.txt 2>&1 || true
+// RUN: FileCheck -dump-input=always --check-prefix=STDERR %s --input-file=%t.txt
+// RUN: FileCheck -dump-input=always --check-prefix=SARIF  %s --input-file=%t/sarif-output.sarif
+
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: %clang -Wall -Wextra -fdiagnostics-format=sarif-file %s %s -o %t/sarif-output > %t.txt 2>&1 || true
+// RUN: FileCheck -dump-input=always --check-prefix=STDERR %s --input-file=%t.txt
+// RUN: FileCheck -dump-input=always --check-prefix=SARIF  %s --input-file=%t/sarif-output.sarif
+// RUN: FileCheck -dump-input=always --check-prefix=SARIF2 %s --input-file=%t/sarif-output.sarif
+
 // STDERR: warning: diagnostic formatting in SARIF mode is currently unstable [-Wsarif-format-unstable]
 // SARIF: {
 // SARIF:   "$schema":"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json";,
@@ -489,6 +506,460 @@
 // SARIF:         }
 // SARIF:       }
 // SARIF:     }
+// SARIF2:    ,
+// SARIF2:    {
+// SARIF2:      "artifacts":[
+// SARIF2:        {
+// SARIF2:          "length":{{[0-9]+}},
+// SARIF2:          "location":{
+// SARIF2:            "index":0,
+// SARIF2:            "uri":{{"file://[^"]+/clang/test/Frontend/sarif-diagnostics.cpp"}}
+// SARIF2:          },
+// SARIF2:          "mimeType":"text/plain",
+// SARIF2:          "roles":[
+// SARIF2:            "resultFile"
+// SARIF2:          ]
+// SARIF2:        },
+// SARIF2:        {
+// SARIF2:          "length":{{[0-9]+}},
+// SARIF2:          "location":{
+// SARIF2:            "index":1,
+// SARIF2:            "uri":"file://{{[^"]+test/Frontend/sarif-diagnostics.hpp}}"
+// SARIF2:          },
+// SARIF2:          "mimeType":"text/plain",
+// SARIF2:          "roles":[
+// SARIF2:            "resultFile"
+// SARIF2:          ]
+// SARIF2:        }
+// SARIF2:      ],
+// SARIF2:      "columnKind":"unicodeCodePoints",
+// SARIF2:      "results":[
+// SARIF2:        {
+// SARIF2:          "level":"error",
+// SARIF2:          "locations":[
+// SARIF2:            {
+// SARIF2:              "physicalLocation":{
+// SARIF2:                "artifactLocation":{
+// SARIF2:                  "index":0,
+// SARIF2:                  "uri":{{"file://[^"]+/clang/test/Frontend/sarif-diagnostics.cpp"}}
+// SARIF2:                },
+// SARIF2:                "region":{
+// SARIF2:                  "endColumn":1,
+// SARIF2:                  "startColumn":1,
+// SARIF2:                  "startLine":12
+// SARIF2:                }
+// SARIF2:              }
+// SARIF2:            }
+// SARIF2:          ],
+// SARIF2:          "message":{
+// SARIF2:            "text":"'main' must return 'int'"
+// SARIF2:          },
+// SARIF2:          "ruleId":"3485",
+// SARIF2:          "ruleIndex":0
+// SARIF2:        },
+// SARIF2:        {
+// SARIF2:          "level":"error",
+// SARIF2:          "locations":[
+// SARIF2:            {
+// SARIF2:              "physicalLocation":{
+// SARIF2:                "artifactLocation":{
+// SARIF2:                  "index":0,
+// SARIF2:                  "uri":{{"file://[^"]+/clang/test/Frontend/sarif-diagnostics.cpp"}}
+// SARIF2:                },
+// SARIF2:                "region":{
+// SARIF2:                  "endColumn":11,
+// SARIF2:                  "startColumn":11,
+// SARIF2:                  "startLine":13
+// SARIF2:                }
+// SARIF2:              }
+// SARIF2:            }
+// SARIF2:          ],
+// SARIF2:          "message":{
+// SARIF2:            "text":"use of undeclared identifier 'hello'"
+// SARIF2:          },
+// SARIF2:          "ruleId":"4632",
+// SARIF2:          "ruleIndex":1
+// SARIF2:        },
+// SARIF2:        {
+// SARIF2:          "level":"error",
+// SARIF2:          "locations":[
+// SARIF2:            {
+// SARIF2:              "physicalLocation":{
+// SARIF2:                "artifactLocation":{
+// SARIF2:                  "index":0,
+// SARIF2:                  "uri":{{"file://[^"]+/clang/test/Frontend/sarif-diagnostics.cpp"}}
+// SARIF2:                },
+// SARIF2:                "region":{
+// SARIF2:                  "endColumn":17,
+// SARIF2:                  "startColumn":17,
+// SARIF2:                  "startLine":15
+// SARIF2:                }
+// SARIF2:              }
+// SARIF2:            }
+// SARIF2:          ],
+// SARIF2:          "message":{
+// SARIF2:            "text":"invalid digit 'a' in decimal constant"
+// SARIF2:          },
+// SARIF2:          "ruleId":"898",
+// SARIF2:          "ruleIndex":2
+// SARIF2:        },
+// SARIF2:        {
+// SARIF2:          "level":"warning",
+// SARIF2:          "locations":[
+// SARIF2:            {
+// SARIF2:              "physicalLocation":{
+// SARIF2:                "artifactLocation":{
+// SARIF2:                  "index":0,
+// SARIF2:                  "uri":{{"file://[^"]+clang/test/Frontend/sarif-diagnostics.cpp"}}
+// SARIF2:                },
+// SARIF2:                "region":{
+// SARIF2:                  "endColumn":5,
+// SARIF2:                  "startColumn":5,
+// SARIF2:                  "startLine":19
+// SARIF2:                }
+// SARIF2:              }
+// SARIF2:            }
+// SARIF2:          ],
+// SARIF2:          "message":{
+// SARIF2:            "text":"misleading indentation; statement is not part of the previous 'if'"
+// SARIF2:          },
+// SARIF2:          "ruleId":"1826",
+// SARIF2:          "ruleIndex":3
+// SARIF2:        },
+// SARIF2:        {
+// SARIF2:          "level":"note",
+// SARIF2:          "locations":[
+// SARIF2:            {
+// SARIF2:              "physicalLocation":{
+// SARIF2:                "artifactLocation":{
+// SARIF2:                  "index":0,
+// SARIF2:                  "uri":{{"file://[^"]+/clang/test/Frontend/sarif-diagnostics.cpp"}}
+// SARIF2:                },
+// SARIF2:                "region":{
+// SARIF2:                  "endColumn":3,
+// SARIF2:                  "startColumn":3,
+// SARIF2:                  "startLine":17
+// SARIF2:                }
+// SARIF2:              }
+// SARIF2:            }
+// SARIF2:          ],
+// SARIF2:          "message":{
+// SARIF2:            "text":"previous statement is here"
+// SARIF2:          },
+// SARIF2:          "ruleId":"1746",
+// SARIF2:          "ruleIndex":4
+// SARIF2:        },
+// SARIF2:        {
+// SARIF2:          "level":"warning",
+// SARIF2:          "locations":[
+// SARIF2:            {
+// SARIF2:              "physicalLocation":{
+// SARIF2:                "artifactLocation":{
+// SARIF2:                  "index":0,
+// SARIF2:                  "uri":{{"file://[^"]+clang/test/Frontend/sarif-diagnostics.cpp"}}
+// SARIF2:                },
+// SARIF2:                "region":{
+// SARIF2:                  "endColumn":10,
+// SARIF2:                  "startColumn":10,
+// SARIF2:                  "startLine":18
+// SARIF2:                }
+// SARIF2:              }
+// SARIF2:            }
+// SARIF2:          ],
+// SARIF2:          "message":{
+// SARIF2:            "text":"unused variable 'Yes'"
+// SARIF2:          },
+// SARIF2:          "ruleId":"6593",
+// SARIF2:          "ruleIndex":5
+// SARIF2:        },
+// SARIF2:        {
+// SARIF2:          "level":"error",
+// SARIF2:          "locations":[
+// SARIF2:            {
+// SARIF2:              "physicalLocation":{
+// SARIF2:                "artifactLocation":{
+// SARIF2:                  "index":0,
+// SARIF2:                  "uri":{{"file://[^"]+clang/test/Frontend/sarif-diagnostics.cpp"}}
+// SARIF2:                },
+// SARIF2:                "region":{
+// SARIF2:                  "endColumn":12,
+// SARIF2:                  "startColumn":12,
+// SARIF2:                  "startLine":21
+// SARIF2:                }
+// SARIF2:              }
+// SARIF2:            }
+// SARIF2:          ],
+// SARIF2:          "message":{
+// SARIF2:            "text":"use of undeclared identifier 'hi'"
+// SARIF2:          },
+// SARIF2:          "ruleId":"4632",
+// SARIF2:          "ruleIndex":6
+// SARIF2:        },
+// SARIF2:        {
+// SARIF2:          "level":"error",
+// SARIF2:          "locations":[
+// SARIF2:            {
+// SARIF2:              "physicalLocation":{
+// SARIF2:                "artifactLocation":{
+// SARIF2:                  "index":0,
+// SARIF2:                  "uri":{{"file://[^"]+/clang/test/Frontend/sarif-diagnostics.cpp"}}
+// SARIF2:                },
+// SARIF2:                "region":{
+// SARIF2:                  "endColumn":1,
+// SARIF2:                  "startColumn":1,
+// SARIF2:                  "startLine":23
+// SARIF2:                }
+// SARIF2:              }
+// SARIF2:            }
+// SARIF2:          ],
+// SARIF2:          "message":{
+// SARIF2:            "text":"extraneous closing brace ('}')"
+// SARIF2:          },
+// SARIF2:          "ruleId":"1400",
+// SARIF2:          "ruleIndex":7
+// SARIF2:        },
+// SARIF2:        {
+// SARIF2:          "level":"error",
+// SARIF2:          "locations":[
+// SARIF2:            {
+// SARIF2:              "physicalLocation":{
+// SARIF2:                "artifactLocation":{
+// SARIF2:                  "index":0,
+// SARIF2:                  "uri":{{"file://[^"]+/clang/test/Frontend/sarif-diagnostics.cpp"}}
+// SARIF2:                },
+// SARIF2:                "region":{
+// SARIF2:                  "endColumn":6,
+// SARIF2:                  "endLine":27,
+// SARIF2:                  "startColumn":5,
+// SARIF2:                  "startLine":27
+// SARIF2:                }
+// SARIF2:              }
+// SARIF2:            },
+// SARIF2:            {
+// SARIF2:              "physicalLocation":{
+// SARIF2:                "artifactLocation":{
+// SARIF2:                  "index":0,
+// SARIF2:                  "uri":{{"file://[^"]+/clang/test/Frontend/sarif-diagnostics.cpp"}}
+// SARIF2:                },
+// SARIF2:                "region":{
+// SARIF2:                  "endColumn":10,
+// SARIF2:                  "endLine":27,
+// SARIF2:                  "startColumn":9,
+// SARIF2:                  "startLine":27
+// SARIF2:                }
+// SARIF2:              }
+// SARIF2:            },
+// SARIF2:            {
+// SARIF2:              "physicalLocation":{
+// SARIF2:                "artifactLocation":{
+// SARIF2:                  "index":0,
+// SARIF2:                  "uri":{{"file://[^"]+/clang/test/Frontend/sarif-diagnostics.cpp"}}
+// SARIF2:                },
+// SARIF2:                "region":{
+// SARIF2:                  "endColumn":7,
+// SARIF2:                  "startColumn":7,
+// SARIF2:                  "startLine":27
+// SARIF2:                }
+// SARIF2:              }
+// SARIF2:            }
+// SARIF2:          ],
+// SARIF2:          "message":{
+// SARIF2:            "text":"invalid operands to binary expression ('t1' and 't1')"
+// SARIF2:          },
+// SARIF2:          "ruleId":"4567",
+// SARIF2:          "ruleIndex":8
+// SARIF2:        },
+// SARIF2:        {
+// SARIF2:          "level":"note",
+// SARIF2:          "locations":[
+// SARIF2:            {
+// SARIF2:              "physicalLocation":{
+// SARIF2:                "artifactLocation":{
+// SARIF2:                  "index":0,
+// SARIF2:                  "uri":{{"file://[^"]+/clang/test/Frontend/sarif-diagnostics.cpp"}}
+// SARIF2:                },
+// SARIF2:                "region":{
+// SARIF2:                  "endColumn":10,
+// SARIF2:                  "startColumn":10,
+// SARIF2:                  "startLine":30
+// SARIF2:                }
+// SARIF2:              }
+// SARIF2:            }
+// SARIF2:          ],
+// SARIF2:          "message":{
+// SARIF2:            "text":"in file included from {{[^"]+test/Frontend/sarif-diagnostics.cpp:30:}}\n"
+// SARIF2:          },
+// SARIF2:          "ruleId":"-1",
+// SARIF2:          "ruleIndex":9
+// SARIF2:        },
+// SARIF2:        {
+// SARIF2:          "level":"error",
+// SARIF2:          "locations":[
+// SARIF2:            {
+// SARIF2:              "physicalLocation":{
+// SARIF2:                "artifactLocation":{
+// SARIF2:                  "index":1,
+// SARIF2:                  "uri":"file:///{{[^"]+/test/Frontend/sarif-diagnostics.hpp}}"
+// SARIF2:                },
+// SARIF2:                "region":{
+// SARIF2:                  "endColumn":1,
+// SARIF2:                  "startColumn":1,
+// SARIF2:                  "startLine":1
+// SARIF2:                }
+// SARIF2:              }
+// SARIF2:            }
+// SARIF2:          ],
+// SARIF2:          "message":{
+// SARIF2:            "text":"unknown type name 'Test'"
+// SARIF2:          },
+// SARIF2:          "ruleId":"4657",
+// SARIF2:          "ruleIndex":10
+// SARIF2:        }
+// SARIF2:      ],
+// SARIF2:      "tool":{
+// SARIF2:        "driver":{
+// SARIF2:          "fullName":"",
+// SARIF2:          "informationUri":"https://clang.llvm.org/docs/UsersManual.html";,
+// SARIF2:          "language":"en-US",
+// SARIF2:          "name":"clang",
+// SARIF2:          "rules":[
+// SARIF2:            {
+// SARIF2:              "defaultConfiguration":{
+// SARIF2:                "enabled":true,
+// SARIF2:                "level":"error",
+// SARIF2:                "rank":50
+// SARIF2:              },
+// SARIF2:              "fullDescription":{
+// SARIF2:                "text":""
+// SARIF2:              },
+// SARIF2:              "id":"3485",
+// SARIF2:              "name":""
+// SARIF2:            },
+// SARIF2:            {
+// SARIF2:              "defaultConfiguration":{
+// SARIF2:                "enabled":true,
+// SARIF2:                "level":"error",
+// SARIF2:                "rank":50
+// SARIF2:              },
+// SARIF2:              "fullDescription":{
+// SARIF2:                "text":""
+// SARIF2:              },
+// SARIF2:              "id":"4632",
+// SARIF2:              "name":""
+// SARIF2:            },
+// SARIF2:            {
+// SARIF2:              "defaultConfiguration":{
+// SARIF2:                "enabled":true,
+// SARIF2:                "level":"error",
+// SARIF2:                "rank":50
+// SARIF2:              },
+// SARIF2:              "fullDescription":{
+// SARIF2:                "text":""
+// SARIF2:              },
+// SARIF2:              "id":"898",
+// SARIF2:              "name":""
+// SARIF2:            },
+// SARIF2:            {
+// SARIF2:              "defaultConfiguration":{
+// SARIF2:                "enabled":true,
+// SARIF2:                "level":"warning",
+// SARIF2:                "rank":-1
+// SARIF2:              },
+// SARIF2:              "fullDescription":{
+// SARIF2:                "text":""
+// SARIF2:              },
+// SARIF2:              "id":"1826",
+// SARIF2:              "name":""
+// SARIF2:            },
+// SARIF2:            {
+// SARIF2:              "defaultConfiguration":{
+// SARIF2:                "enabled":true,
+// SARIF2:                "level":"note",
+// SARIF2:                "rank":-1
+// SARIF2:              },
+// SARIF2:              "fullDescription":{
+// SARIF2:                "text":""
+// SARIF2:              },
+// SARIF2:              "id":"1746",
+// SARIF2:              "name":""
+// SARIF2:            },
+// SARIF2:            {
+// SARIF2:              "defaultConfiguration":{
+// SARIF2:                "enabled":true,
+// SARIF2:                "level":"warning",
+// SARIF2:                "rank":-1
+// SARIF2:              },
+// SARIF2:              "fullDescription":{
+// SARIF2:                "text":""
+// SARIF2:              },
+// SARIF2:              "id":"6593",
+// SARIF2:              "name":""
+// SARIF2:            },
+// SARIF2:            {
+// SARIF2:              "defaultConfiguration":{
+// SARIF2:                "enabled":true,
+// SARIF2:                "level":"error",
+// SARIF2:                "rank":50
+// SARIF2:              },
+// SARIF2:              "fullDescription":{
+// SARIF2:                "text":""
+// SARIF2:              },
+// SARIF2:              "id":"4632",
+// SARIF2:              "name":""
+// SARIF2:            },
+// SARIF2:            {
+// SARIF2:              "defaultConfiguration":{
+// SARIF2:                "enabled":true,
+// SARIF2:                "level":"error",
+// SARIF2:                "rank":50
+// SARIF2:              },
+// SARIF2:              "fullDescription":{
+// SARIF2:                "text":""
+// SARIF2:              },
+// SARIF2:              "id":"1400",
+// SARIF2:              "name":""
+// SARIF2:            },
+// SARIF2:            {
+// SARIF2:              "defaultConfiguration":{
+// SARIF2:                "enabled":true,
+// SARIF2:                "level":"error",
+// SARIF2:                "rank":50
+// SARIF2:              },
+// SARIF2:              "fullDescription":{
+// SARIF2:                "text":""
+// SARIF2:              },
+// SARIF2:              "id":"4567",
+// SARIF2:              "name":""
+// SARIF2:            },
+// SARIF2:            {
+// SARIF2:              "defaultConfiguration":{
+// SARIF2:                "enabled":true,
+// SARIF2:                "level":"note",
+// SARIF2:                "rank":-1
+// SARIF2:              },
+// SARIF2:              "fullDescription":{
+// SARIF2:                "text":""
+// SARIF2:              },
+// SARIF2:              "id":"-1",
+// SARIF2:              "name":""
+// SARIF2:            },
+// SARIF2:            {
+// SARIF2:              "defaultConfiguration":{
+// SARIF2:                "enabled":true,
+// SARIF2:                "level":"error",
+// SARIF2:                "rank":50
+// SARIF2:              },
+// SARIF2:              "fullDescription":{
+// SARIF2:                "text":""
+// SARIF2:              },
+// SARIF2:              "id":"4657",
+// SARIF2:              "name":""
+// SARIF2:            }
+// SARIF2:          ],
+// SARIF2:          "version":"17.0.0"
+// SARIF2:        }
+// SARIF2:      }
+// SARIF2:    }
 // SARIF:   ],
 // SARIF:   "version":"2.1.0"
 // SARIF: }
Index: clang/lib/Tooling/SarifLinker.cpp
===================================================================
--- /dev/null
+++ clang/lib/Tooling/SarifLinker.cpp
@@ -0,0 +1,125 @@
+#include "clang/Basic/Sarif.h"
+#include "clang/Config/config.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/Process.h"
+#include <cassert>
+#include <cstdlib>
+#include <fstream>
+#include <iterator>
+#include <string>
+#include <string_view>
+#include <system_error>
+
+namespace llvm::sarif_linker {
+[[noreturn]] void ReportErrorAndExit(llvm::errc error_code, StringRef Message) {
+  llvm::errs() << "sarif-ld: error: " << Message << '\n';
+  std::exit(static_cast<int>(error_code));
+}
+
+static json::Value ReadSarifDiagnostics(StringRef Path) {
+  std::ifstream in(Path.data());
+  if (!in) {
+    ReportErrorAndExit(llvm::errc::io_error, (Path + " not found").str());
+  }
+
+  in >> std::noskipws;
+  std::string data(std::istream_iterator<char>(in), {});
+  assert(!in);
+  if (!in.eof()) {
+    ReportErrorAndExit(llvm::errc::io_error, "Unable to read " + Path.str());
+  }
+
+  llvm::Expected<json::Value> value = json::parse(data);
+  if (!value) {
+    ReportErrorAndExit(llvm::errc::invalid_argument,
+                       "SARIF requires a valid JSON format");
+  }
+
+  // FIXME: we should eventually replace this with a proper SARIF validation
+  // algorithm. For now, we're just checking that a few fields are present
+  // (namely to ensure it "looks" like a SARIF file, and to check the fields
+  // we need are around).
+  json::Object *object = value->getAsObject();
+  if (object == nullptr) {
+    ReportErrorAndExit(llvm::errc::invalid_argument,
+                       "SARIF requires the top-level value to be an object");
+  }
+
+  if (object->getString("$schema") == std::nullopt) {
+    ReportErrorAndExit(llvm::errc::invalid_argument,
+                       "SARIF requires there to be a `$schema` string field");
+  }
+
+  if (object->getString("version") == std::nullopt) {
+    ReportErrorAndExit(llvm::errc::invalid_argument,
+                       "SARIF requires there to be a `version` string field");
+  }
+
+  if (object->getArray("runs") == nullptr) {
+    ReportErrorAndExit(llvm::errc::invalid_argument,
+                       "SARIF requires there to be a `runs` array field");
+  }
+
+  return *value;
+}
+
+template <class JsonValue> static auto &getRuns(JsonValue &value) {
+  return *value.getAsObject()->getArray("runs")->front().getAsObject();
+}
+
+static json::Value ReduceSarif(json::Value Init,
+                               ArrayRef<json::Value> SarifObjects) {
+  json::Object &FirstRuns = getRuns(Init);
+  json::Array &FirstArtifacts = *FirstRuns.getArray("artifacts");
+  json::Array &FirstResults = *FirstRuns.getArray("results");
+
+  for (const json::Value &Other : SarifObjects) {
+    const json::Object &OtherRuns = getRuns(Other);
+    const json::Array &Artifacts = *OtherRuns.getArray("artifacts");
+    FirstArtifacts.insert(FirstArtifacts.end(), Artifacts.begin(),
+                          Artifacts.end());
+
+    const json::Array &Results = *OtherRuns.getArray("results");
+    FirstResults.insert(FirstResults.end(), Results.begin(), Results.end());
+  }
+
+  return Init;
+}
+
+std::error_code LinkSarifDiagnostics(const opt::ArgStringList &InputPaths,
+                                     StringRef OutputPath) {
+  assert(InputPaths.size() > 0 && "There needs to be at least one Input path.");
+  std::vector<std::string> SarifDiagnosticPaths;
+  llvm::copy_if(InputPaths, std::back_inserter(SarifDiagnosticPaths),
+                [](StringRef File) { return File.ends_with(".o"); });
+  llvm::transform(SarifDiagnosticPaths, SarifDiagnosticPaths.begin(),
+                  [](const std::string &Temp) { return Temp + ".sarif"; });
+
+  std::vector<json::Value> JSONData;
+  llvm::transform(SarifDiagnosticPaths, std::back_inserter(JSONData),
+                  [](StringRef Path) {
+                    json::Value SarifDiagnostics = ReadSarifDiagnostics(Path);
+                    return SarifDiagnostics;
+                  });
+
+  json::Value Output = ReduceSarif(
+      std::move(JSONData[0]), ArrayRef<json::Value>(JSONData).drop_front());
+
+  std::error_code EC;
+  llvm::raw_fd_ostream Out(OutputPath.str() + ".sarif", EC);
+  if (EC) {
+    return EC;
+  }
+
+  Out << Output;
+  return EC;
+}
+} // namespace llvm::sarif_linker
Index: clang/lib/Tooling/CMakeLists.txt
===================================================================
--- clang/lib/Tooling/CMakeLists.txt
+++ clang/lib/Tooling/CMakeLists.txt
@@ -113,6 +113,7 @@
   JSONCompilationDatabase.cpp
   Refactoring.cpp
   RefactoringCallbacks.cpp
+  SarifLinker.cpp
   StandaloneExecution.cpp
   NodeIntrospection.cpp
   ${BINARY_INCLUDE_DIR}/NodeIntrospection.inc
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D145438: PLEA... Christopher Di Bella via Phabricator via cfe-commits

Reply via email to