abrahamcd updated this revision to Diff 452840.
abrahamcd added a comment.

Minor cleanup.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131632/new/

https://reviews.llvm.org/D131632

Files:
  clang/include/clang/Frontend/SARIFDiagnostic.h
  clang/include/clang/Frontend/SARIFDiagnosticPrinter.h
  clang/lib/Frontend/CMakeLists.txt
  clang/lib/Frontend/CompilerInstance.cpp
  clang/lib/Frontend/FrontendAction.cpp
  clang/lib/Frontend/SARIFDiagnostic.cpp
  clang/lib/Frontend/SARIFDiagnosticPrinter.cpp
  clang/test/Frontend/sarif-diagnostics.cpp
  clang/unittests/Frontend/CMakeLists.txt
  clang/unittests/Frontend/SARIFDiagnosticTest.cpp

Index: clang/unittests/Frontend/SARIFDiagnosticTest.cpp
===================================================================
--- /dev/null
+++ clang/unittests/Frontend/SARIFDiagnosticTest.cpp
@@ -0,0 +1,137 @@
+// RUN: %clang -fdiagnostics-format=sarif %s -o %t.exe -DGTEST
+// RUN: %clang -fsyntax-only -Wall -Wextra -fdiagnostics-format=sarif %s 2>
+// %t.diags || true RUN: %t.exe < %t.diags
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Program.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <filesystem>
+#include <vector>
+
+namespace {
+
+constexpr llvm::StringRef BrokenProgram = R"(// Example errors below start on line 2
+void main() {
+  int i = hello;
+
+  float test = 1a.0;
+
+  if (true)
+    bool Yes = true;
+    return;
+
+  bool j = hi;
+}
+})";
+
+TEST(SARIFDiagnosticTest, TestFields) {
+  llvm::SmallString<256> SearchDir;
+  llvm::sys::fs::current_path(SearchDir);
+  
+  SearchDir.append("/../../../bin");
+  // ASSERT_EQ(SearchDir.str(), "hi");
+  llvm::ErrorOr<std::string> ClangPathOrErr =
+  llvm::sys::findProgramByName("clang", {SearchDir});
+  ASSERT_TRUE(ClangPathOrErr);
+  const std::string &ClangPath = *ClangPathOrErr;
+  // ASSERT_EQ(ClangPath, "hi");
+
+  llvm::ErrorOr<std::string> EchoPathOrErr =
+      llvm::sys::findProgramByName("echo");
+  ASSERT_TRUE(EchoPathOrErr);
+  const std::string &EchoPath = *EchoPathOrErr;
+
+  int EchoInputFD;
+  llvm::SmallString<32> EchoInputFile, EchoOutputFile;
+  llvm::sys::fs::createTemporaryFile("echo-input", "", EchoInputFD,
+                                     EchoInputFile);
+  llvm::sys::fs::createTemporaryFile("echo-output", "", EchoOutputFile);
+  llvm::FileRemover InputRemover(EchoInputFile.c_str());
+  llvm::FileRemover OutputRemover(EchoOutputFile.c_str());
+
+  llvm::Optional<llvm::StringRef> Redirects[] = {
+      EchoInputFile.str(), EchoOutputFile.str(), llvm::StringRef("")};
+
+  int RunResult = llvm::sys::ExecuteAndWait(EchoPath, {"echo", BrokenProgram},
+                                            llvm::None, Redirects);
+  ASSERT_EQ(RunResult, 0);
+
+  // auto EchoOutputBuf = llvm::MemoryBuffer::getFile(EchoOutputFile.c_str());
+  // ASSERT_TRUE(EchoOutputBuf);
+  // llvm::StringRef EchoOutput = EchoOutputBuf.get()->getBuffer();
+  // ASSERT_EQ(EchoOutput.str(), "hi");
+
+  llvm::SmallString<32> ClangErrFile;
+  llvm::sys::fs::createTemporaryFile("clang-err", "", ClangErrFile);
+  llvm::FileRemover ClangErrRemover(ClangErrFile.c_str());
+
+  llvm::Optional<llvm::StringRef> ClangRedirects[] = {
+      EchoOutputFile.str(), llvm::StringRef(""), ClangErrFile.str()};
+  llvm::StringRef Args[] = {"clang",
+                            "-xc++",
+                            "-",
+                            "-fsyntax-only",
+                            "-Wall",
+                            "-Wextra",
+                            "-fdiagnostics-format=sarif"};
+
+  int ClangResult =
+      llvm::sys::ExecuteAndWait(ClangPath, Args, llvm::None, ClangRedirects);
+  ASSERT_EQ(ClangResult, 1);
+
+  // auto ClangOutputBuf = llvm::MemoryBuffer::getFile(ClangOutputFile.c_str());
+  // ASSERT_TRUE(ClangOutputBuf);
+  // llvm::StringRef ClangOutput = ClangOutputBuf.get()->getBuffer();
+  // ASSERT_EQ(ClangOutput.str(), "hi");
+
+  auto ClangErrBuf = llvm::MemoryBuffer::getFile(ClangErrFile.c_str());
+  ASSERT_TRUE(ClangErrBuf);
+  llvm::StringRef ClangErr = ClangErrBuf.get()->getBuffer();
+  ASSERT_EQ(ClangErr.str(), "hi");
+
+  llvm::Expected<llvm::json::Value> Value = llvm::json::parse(ClangErr.str());
+  ASSERT_FALSE(!Value);
+
+  llvm::json::Object *SarifDoc = Value->getAsObject();
+
+  const llvm::json::Array *Runs = SarifDoc->getArray("runs");
+  const llvm::json::Object *TheRun = Runs->back().getAsObject();
+  const llvm::json::Array *Results = TheRun->getArray("results");
+  
+  // Check Artifacts
+  const llvm::json::Array *Artifacts = TheRun->getArray("artifacts");
+  const llvm::json::Object *TheArtifact = Artifacts->back().getAsObject();
+  const llvm::json::Object *Location = TheArtifact->getObject("location");
+
+  ASSERT_TRUE(Location->getInteger("index").has_value());
+  ASSERT_TRUE(Location->getString("uri").has_value());
+
+  EXPECT_EQ(Location->getInteger("index").value(), 0);
+  EXPECT_EQ(Location->getString("uri").value(), "file://<stdin>");
+
+  // Check Driver
+  const llvm::json::Object *Driver =
+      TheRun->getObject("tool")->getObject("driver");
+
+  ASSERT_TRUE(Driver->getString("name").has_value());
+  ASSERT_TRUE(Driver->getString("fullName").has_value());
+
+  EXPECT_EQ(Driver->getString("name").value(), "clang");
+  EXPECT_EQ(Driver->getString("fullName").value(), "clang-15");
+
+  // Check Rules
+  const llvm::json::Array *Rules = Driver->getArray("rules");
+  std::vector<std::string> IDs;
+
+
+
+
+}
+
+} // namespace
Index: clang/unittests/Frontend/CMakeLists.txt
===================================================================
--- clang/unittests/Frontend/CMakeLists.txt
+++ clang/unittests/Frontend/CMakeLists.txt
@@ -12,6 +12,7 @@
   ParsedSourceLocationTest.cpp
   PCHPreambleTest.cpp
   OutputStreamTest.cpp
+  SARIFDiagnosticTest.cpp
   TextDiagnosticTest.cpp
   UtilsTest.cpp
   )
Index: clang/test/Frontend/sarif-diagnostics.cpp
===================================================================
--- /dev/null
+++ clang/test/Frontend/sarif-diagnostics.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang -fsyntax-only -Wall -Wextra -fdiagnostics-format=sarif %s > %t 2>&1 || true
+// RUN: FileCheck -dump-input=always %s --input-file=%t
+// CHECK: {"$schema":"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json","runs":[{"artifacts":[{"length":2953,"location":{"index":0,"uri":"file:///usr/local/google/home/abrahamcd/projects/llvm-project/clang/test/Frontend/sarif-diagnostics.cpp"},"mimeType":"text/plain","roles":["resultFile"]}],"columnKind":"unicodeCodePoints","results":[{"locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":1,"startColumn":1,"startLine":5}}}],"message":{"text":";'main' must return 'int'"},"ruleId":"3463","ruleIndex":0},{"locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":11,"startColumn":11,"startLine":6}}}],"message":{"text":"use of undeclared identifier 'hello'"},"ruleId":"4601","ruleIndex":1},{"locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":17,"startColumn":17,"startLine":8}}}],"message":{"text":"invalid digit 'a' in decimal constant"},"ruleId":"898","ruleIndex":2},{"locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":5,"startColumn":5,"startLine":12}}}],"message":{"text":"misleading indentation; statement is not part of the previous 'if'"},"ruleId":"1806","ruleIndex":3},{"locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":3,"startColumn":3,"startLine":10}}}],"message":{"text":"previous statement is here"},"ruleId":"1730","ruleIndex":4},{"locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":10,"startColumn":10,"startLine":11}}}],"message":{"text":"unused variable 'Yes'"},"ruleId":"6536","ruleIndex":5},{"locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":12,"startColumn":12,"startLine":14}}}],"message":{"text":"use of undeclared identifier 'hi'"},"ruleId":"4601","ruleIndex":6},{"locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":1,"startColumn":1,"startLine":16}}}],"message":{"text":"extraneous closing brace ('}')"},"ruleId":"1399","ruleIndex":7}],"tool":{"driver":{"fullName":"","informationUri":"https://clang.llvm.org/docs/UsersManual.html","language":"en-US","name":"clang","rules":[{"fullDescription":{"text":""},"id":"3463","name":""},{"fullDescription":{"text":""},"id":"4601","name":""},{"fullDescription":{"text":""},"id":"898","name":""},{"fullDescription":{"text":""},"id":"1806","name":""},{"fullDescription":{"text":""},"id":"1730","name":""},{"fullDescription":{"text":""},"id":"6536","name":""},{"fullDescription":{"text":""},"id":"4601","name":""},{"fullDescription":{"text":""},"id":"1399","name":""}],"version":"16.0.0"}}}],"version":"2.1.0"}
+
+void main() {
+  int i = hello;
+
+  float test = 1a.0;
+
+  if (true)
+    bool Yes = true;
+    return;
+
+  bool j = hi;
+}
+}
Index: clang/lib/Frontend/SARIFDiagnosticPrinter.cpp
===================================================================
--- /dev/null
+++ clang/lib/Frontend/SARIFDiagnosticPrinter.cpp
@@ -0,0 +1,87 @@
+//===------- SARIFDiagnosticPrinter.cpp - Diagnostic Printer----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This diagnostic client prints out their diagnostic messages in SARIF format.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Frontend/SARIFDiagnosticPrinter.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/Sarif.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/DiagnosticRenderer.h"
+#include "clang/Frontend/SARIFDiagnostic.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+using namespace clang;
+
+SARIFDiagnosticPrinter::SARIFDiagnosticPrinter(raw_ostream &os,
+                                               DiagnosticOptions *diags,
+                                               bool _OwnsOutputStream)
+    : OS(os), DiagOpts(diags), OwnsOutputStream(_OwnsOutputStream) {}
+
+SARIFDiagnosticPrinter::~SARIFDiagnosticPrinter() {
+  if (OwnsOutputStream)
+    delete &OS;
+}
+
+void SARIFDiagnosticPrinter::BeginSourceFile(const LangOptions &LO,
+                                             const Preprocessor *PP) {
+  // Build the SARIFDiagnostic utility.
+  assert(hasSarifWriter() && "Writer not set!");
+  SARIFDiag.reset(new SARIFDiagnostic(OS, LO, &*DiagOpts, &*Writer));
+  // Initialize the SARIF object.
+  Writer->createRun("clang", Prefix);
+}
+
+void SARIFDiagnosticPrinter::EndSourceFile() {
+  Writer->endRun();
+  llvm::json::Value value(Writer->createDocument());
+  OS << "\n" << value << "\n\n";
+  OS.flush();
+  SARIFDiag.reset();
+}
+
+void SARIFDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
+                                              const Diagnostic &Info) {
+  // Default implementation (Warnings/errors count). // Keeps track of the
+  // number of errors
+  DiagnosticConsumer::HandleDiagnostic(Level, Info);
+
+  // Render the diagnostic message into a temporary buffer eagerly. We'll use
+  // this later as we add the diagnostic to the SARIF object.
+  SmallString<100> OutStr;
+  Info.FormatDiagnostic(OutStr);
+
+  llvm::raw_svector_ostream DiagMessageStream(OutStr);
+
+  // Use a dedicated, simpler path for diagnostics without a valid location.
+  // This is important as if the location is missing, we may be emitting
+  // diagnostics in a context that lacks language options, a source manager, or
+  // other infrastructure necessary when emitting more rich diagnostics.
+  if (!Info.getLocation().isValid()) { // TODO: What is this case? 
+    // SARIFDiag->addDiagnosticWithoutLocation(
+    //     DiagMessageStream.str(), Info.getID());
+    return;
+  }
+
+  // Assert that the rest of our infrastructure is setup properly.
+  assert(DiagOpts && "Unexpected diagnostic without options set");
+  assert(Info.hasSourceManager() &&
+         "Unexpected diagnostic with no source manager");
+  assert(SARIFDiag && "Unexpected diagnostic outside source file processing");
+
+  SARIFDiag->emitDiagnostic(
+      FullSourceLoc(Info.getLocation(), Info.getSourceManager()), Level,
+      DiagMessageStream.str(), Info.getRanges(), Info.getFixItHints(), &Info);
+
+}
Index: clang/lib/Frontend/SARIFDiagnostic.cpp
===================================================================
--- /dev/null
+++ clang/lib/Frontend/SARIFDiagnostic.cpp
@@ -0,0 +1,222 @@
+//===------- SARIFDiagnostic.cpp - SARIF Diagnostic Formatting -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Frontend/SARIFDiagnostic.h"
+#include "clang/Basic/CharInfo.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/Sarif.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Locale.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <string>
+
+using namespace clang;
+
+SARIFDiagnostic::SARIFDiagnostic(raw_ostream &OS, const LangOptions &LangOpts,
+                                 DiagnosticOptions *DiagOpts,
+                                 SarifDocumentWriter *Writer)
+    : DiagnosticRenderer(LangOpts, DiagOpts), OS(OS), Writer(Writer) {}
+
+void SARIFDiagnostic::emitDiagnosticMessage(
+    FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level,
+    StringRef Message, ArrayRef<clang::CharSourceRange> Ranges,
+    DiagOrStoredDiag D) {
+
+  auto *Diag = D.dyn_cast<const Diagnostic *>();
+
+  if (!Diag) {
+    return;
+  }
+
+  const SarifRule &Rule =
+      SarifRule::create().setRuleId(std::to_string(Diag->getID()));
+
+  unsigned RuleIdx = Writer->createRule(Rule);
+
+  SarifResult Result =
+      SarifResult::create(RuleIdx).setDiagnosticMessage(Message);
+
+  if (Loc.isValid()) {
+    Result = addLocationToResult(Result, Loc, PLoc, Ranges, *Diag);
+  }
+
+  Writer->appendResult(Result);
+}
+
+SarifResult SARIFDiagnostic::addLocationToResult(
+    SarifResult Result, FullSourceLoc Loc, PresumedLoc PLoc,
+    ArrayRef<CharSourceRange> Ranges, const Diagnostic &Diag) {
+  SmallVector<CharSourceRange> Locations = {};
+
+  if (PLoc.isInvalid()) {
+    // At least add the file name if available:
+    FileID FID = Loc.getFileID();
+    if (FID.isValid()) {
+      if (const FileEntry *FE = Loc.getFileEntry()) {
+        llvm::StringRef Filename =
+            emitFilename(FE->getName(), Loc.getManager());
+        // FIXME: No current way to add file-only location to SARIF object
+      }
+    }
+    return Result;
+  }
+
+  FileID CaretFileID = Loc.getExpansionLoc().getFileID();
+
+  for (ArrayRef<CharSourceRange>::const_iterator RI = Ranges.begin(),
+                                                 RE = Ranges.end();
+       RI != RE; ++RI) {
+    // Ignore invalid ranges.
+    if (!RI->isValid()) {
+      continue;
+    }
+
+    auto &SM = Loc.getManager();
+    SourceLocation B = SM.getExpansionLoc(RI->getBegin());
+    CharSourceRange ERange = SM.getExpansionRange(RI->getEnd());
+    SourceLocation E = ERange.getEnd();
+    bool IsTokenRange = ERange.isTokenRange();
+
+    std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B);
+    std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E);
+
+    // If the start or end of the range is in another file, just discard
+    // it.
+    if (BInfo.first != CaretFileID || EInfo.first != CaretFileID) {
+      continue;
+    }
+
+    // Add in the length of the token, so that we cover multi-char
+    // tokens.
+    unsigned TokSize = 0;
+    if (IsTokenRange)
+      TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts);
+
+    FullSourceLoc BF(B, SM), EF(E, SM);
+    SourceLocation BeginLoc = SM.translateLineCol(
+        BF.getFileID(), BF.getLineNumber(), BF.getColumnNumber());
+    SourceLocation EndLoc = SM.translateLineCol(
+        EF.getFileID(), EF.getLineNumber(), EF.getColumnNumber() + TokSize);
+
+    Locations.push_back(
+        CharSourceRange{SourceRange{BeginLoc, EndLoc}, /* ITR = */ false});
+  }
+
+  auto &SM = Diag.getSourceManager();
+  auto FID = PLoc.getFileID();
+  // Visual Studio 2010 or earlier expects column number to be off by one
+  auto ColNo = (LangOpts.MSCompatibilityVersion &&
+                !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2012))
+                   ? PLoc.getColumn() - 1
+                   : PLoc.getColumn();
+  SourceLocation DiagLoc = SM.translateLineCol(FID, PLoc.getLine(), ColNo);
+
+  Locations.push_back(
+      CharSourceRange{SourceRange{DiagLoc, DiagLoc}, /* ITR = */ false});
+
+  return Result.setLocations(Locations);
+}
+
+/*static*/ void
+SARIFDiagnostic::printDiagnosticLevel(raw_ostream &OS,
+                                      DiagnosticsEngine::Level Level) {
+  // TODO: Change to set Level in Rule once implemented
+  switch (Level) {
+  case DiagnosticsEngine::Ignored:
+    llvm_unreachable("Invalid diagnostic type");
+  case DiagnosticsEngine::Note:
+    break;
+  case DiagnosticsEngine::Remark:
+    break;
+  case DiagnosticsEngine::Warning:
+    break;
+  case DiagnosticsEngine::Error:
+    break;
+  case DiagnosticsEngine::Fatal:
+    break;
+  }
+}
+
+llvm::StringRef SARIFDiagnostic::emitFilename(StringRef Filename,
+                                              const SourceManager &SM) {
+#ifdef _WIN32
+  SmallString<4096> TmpFilename;
+#endif
+  if (DiagOpts->AbsolutePath) {
+    auto File = SM.getFileManager().getFile(Filename);
+    if (File) {
+      // We want to print a simplified absolute path, i. e. without "dots".
+      //
+      // The hardest part here are the paths like "<part1>/<link>/../<part2>".
+      // On Unix-like systems, we cannot just collapse "<link>/..", because
+      // paths are resolved sequentially, and, thereby, the path
+      // "<part1>/<part2>" may point to a different location. That is why
+      // we use FileManager::getCanonicalName(), which expands all indirections
+      // with llvm::sys::fs::real_path() and caches the result.
+      //
+      // On the other hand, it would be better to preserve as much of the
+      // original path as possible, because that helps a user to recognize it.
+      // real_path() expands all links, which sometimes too much. Luckily,
+      // on Windows we can just use llvm::sys::path::remove_dots(), because,
+      // on that system, both aforementioned paths point to the same place.
+#ifdef _WIN32
+      TmpFilename = (*File)->getName();
+      llvm::sys::fs::make_absolute(TmpFilename);
+      llvm::sys::path::native(TmpFilename);
+      llvm::sys::path::remove_dots(TmpFilename, /* remove_dot_dot */ true);
+      Filename = StringRef(TmpFilename.data(), TmpFilename.size());
+#else
+      Filename = SM.getFileManager().getCanonicalName(*File);
+#endif
+    }
+  }
+
+  return Filename;
+}
+
+/// Print out the file/line/column information and include trace.
+///
+/// This method handlen the emission of the diagnostic location information.
+/// This includes extracting as much location information as is present for
+/// the diagnostic and printing it, as well as any include stack or source
+/// ranges necessary.
+void SARIFDiagnostic::emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
+                                        DiagnosticsEngine::Level Level,
+                                        ArrayRef<CharSourceRange> Ranges) {}
+
+void SARIFDiagnostic::emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) {}
+
+void SARIFDiagnostic::emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
+                                         StringRef ModuleName) {}
+
+void SARIFDiagnostic::emitBuildingModuleLocation(FullSourceLoc Loc,
+                                                 PresumedLoc PLoc,
+                                                 StringRef ModuleName) {}
+
+/// Emit a code snippet and caret line.
+///
+/// This routine emits a single line's code snippet and caret line..
+///
+/// \param Loc The location for the caret.
+/// \param Ranges The underlined ranges for this code snippet.
+/// \param Hints The FixIt hints active for this diagnostic.
+void SARIFDiagnostic::emitSnippetAndCaret(
+    FullSourceLoc Loc, DiagnosticsEngine::Level Level,
+    SmallVectorImpl<CharSourceRange> &Ranges, ArrayRef<FixItHint> Hints) {}
Index: clang/lib/Frontend/FrontendAction.cpp
===================================================================
--- clang/lib/Frontend/FrontendAction.cpp
+++ clang/lib/Frontend/FrontendAction.cpp
@@ -11,6 +11,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclGroup.h"
 #include "clang/Basic/Builtins.h"
+#include "clang/Basic/DiagnosticOptions.h"
 #include "clang/Basic/LangStandard.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
@@ -18,6 +19,7 @@
 #include "clang/Frontend/FrontendPluginRegistry.h"
 #include "clang/Frontend/LayoutOverrideSource.h"
 #include "clang/Frontend/MultiplexConsumer.h"
+#include "clang/Frontend/SARIFDiagnosticPrinter.h"
 #include "clang/Frontend/Utils.h"
 #include "clang/Lex/HeaderSearch.h"
 #include "clang/Lex/LiteralSupport.h"
@@ -717,8 +719,14 @@
       return false;
     }
   }
-  if (!CI.hasSourceManager())
+  if (!CI.hasSourceManager()) {
     CI.createSourceManager(CI.getFileManager());
+    if (CI.getDiagnosticOpts().getFormat() == DiagnosticOptions::SARIF) {
+      auto *Writer = new SarifDocumentWriter(CI.getSourceManager());
+      static_cast<SARIFDiagnosticPrinter *>(&CI.getDiagnosticClient())
+          ->setSarifWriter(Writer);
+    }
+  }
 
   // Set up embedding for any specified files. Do this before we load any
   // source files, including the primary module map for the compilation.
Index: clang/lib/Frontend/CompilerInstance.cpp
===================================================================
--- clang/lib/Frontend/CompilerInstance.cpp
+++ 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/DiagnosticOptions.h"
 #include "clang/Basic/FileManager.h"
 #include "clang/Basic/LangStandard.h"
 #include "clang/Basic/SourceManager.h"
@@ -25,6 +26,7 @@
 #include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Frontend/FrontendPluginRegistry.h"
 #include "clang/Frontend/LogDiagnosticPrinter.h"
+#include "clang/Frontend/SARIFDiagnosticPrinter.h"
 #include "clang/Frontend/SerializedDiagnosticPrinter.h"
 #include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Frontend/Utils.h"
@@ -346,6 +348,8 @@
   // implementing -verify.
   if (Client) {
     Diags->setClient(Client, ShouldOwnClient);
+  } else if (Opts->getFormat() == DiagnosticOptions::SARIF) {
+    Diags->setClient(new SARIFDiagnosticPrinter(llvm::errs(), Opts));
   } else
     Diags->setClient(new TextDiagnosticPrinter(llvm::errs(), Opts));
 
Index: clang/lib/Frontend/CMakeLists.txt
===================================================================
--- clang/lib/Frontend/CMakeLists.txt
+++ clang/lib/Frontend/CMakeLists.txt
@@ -31,6 +31,8 @@
   MultiplexConsumer.cpp
   PrecompiledPreamble.cpp
   PrintPreprocessedOutput.cpp
+  SARIFDiagnostic.cpp
+  SARIFDiagnosticPrinter.cpp
   SerializedDiagnosticPrinter.cpp
   SerializedDiagnosticReader.cpp
   TestModuleFileExtension.cpp
Index: clang/include/clang/Frontend/SARIFDiagnosticPrinter.h
===================================================================
--- /dev/null
+++ clang/include/clang/Frontend/SARIFDiagnosticPrinter.h
@@ -0,0 +1,75 @@
+//===--- SARIFDiagnosticPrinter.h - Text Diagnostic Client -------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a concrete diagnostic client, which prints the diagnostics to
+// standard error.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_FRONTEND_SARIFDIAGNOSTICPRINTER_H
+#define LLVM_CLANG_FRONTEND_SARIFDIAGNOSTICPRINTER_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/Sarif.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include <memory>
+
+namespace clang {
+class DiagnosticOptions;
+class LangOptions;
+class SARIFDiagnostic;
+class SarifDocumentWriter;
+
+class SARIFDiagnosticPrinter : public DiagnosticConsumer {
+  raw_ostream &OS;
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
+
+  /// Handle to the currently active text diagnostic emitter.
+  std::unique_ptr<SARIFDiagnostic> SARIFDiag;
+
+  /// A string to prefix to error messages.
+  std::string Prefix;
+
+  SarifDocumentWriter *Writer = nullptr;
+
+  unsigned OwnsOutputStream : 1;
+
+public:
+  SARIFDiagnosticPrinter(raw_ostream &os, DiagnosticOptions *diags,
+                         bool OwnsOutputStream = false);
+  ~SARIFDiagnosticPrinter() override;
+
+  /// setPrefix - Set the diagnostic printer prefix string, which will be
+  /// printed at the start of any diagnostics. If empty, no prefix string is
+  /// used.
+  void setPrefix(std::string Value) {
+    Prefix = std::move(Value);
+  }
+
+  bool hasSarifWriter() const { return Writer != nullptr; }
+
+  SarifDocumentWriter &getSarifWriter() const {
+    assert(Writer && "SarifWriter not set!");
+    return *Writer;
+  }
+
+  void setSarifWriter(SarifDocumentWriter *SarifWriter) {
+    Writer = SarifWriter;
+  }
+
+  void BeginSourceFile(const LangOptions &LO, const Preprocessor *PP) override;
+  void EndSourceFile() override;
+  void HandleDiagnostic(DiagnosticsEngine::Level Level,
+                        const Diagnostic &Info) override;
+};
+
+} // end namespace clang
+
+#endif
Index: clang/include/clang/Frontend/SARIFDiagnostic.h
===================================================================
--- /dev/null
+++ clang/include/clang/Frontend/SARIFDiagnostic.h
@@ -0,0 +1,84 @@
+//===--- SARIFDiagnostic.h - Text Diagnostic Pretty-Printing -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a utility class that provides support for textual pretty-printing of
+// diagnostics. It is used to implement the different code paths which require
+// such functionality in a consistent way.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_FRONTEND_SARIFDIAGNOSTIC_H
+#define LLVM_CLANG_FRONTEND_SARIFDIAGNOSTIC_H
+
+#include "clang/Basic/Sarif.h"
+#include "clang/Frontend/DiagnosticRenderer.h"
+#include "clang/Frontend/TextDiagnostic.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+
+class SARIFDiagnostic : public DiagnosticRenderer {
+  raw_ostream &OS;
+
+  SarifDocumentWriter *Writer;
+
+public:
+  SARIFDiagnostic(raw_ostream &OS,
+                 const LangOptions &LangOpts,
+                 DiagnosticOptions *DiagOpts,
+                 SarifDocumentWriter *Writer);
+
+  ~SARIFDiagnostic() = default;
+
+  /// Print the diagonstic level to a raw_ostream.
+  ///
+  /// This is a static helper that handles colorizing the level and formatting
+  /// it into an arbitrary output stream. This is used internally by the
+  /// SARIFDiagnostic emission code, but it can also be used directly by
+  /// consumers that don't have a source manager or other state that the full
+  /// SARIFDiagnostic logic requires.
+  static void printDiagnosticLevel(raw_ostream &OS,
+                                   DiagnosticsEngine::Level Level);
+
+protected:
+  void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,
+                             DiagnosticsEngine::Level Level, StringRef Message,
+                             ArrayRef<CharSourceRange> Ranges, 
+                             DiagOrStoredDiag D) override;
+
+  void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
+                         DiagnosticsEngine::Level Level,
+                         ArrayRef<CharSourceRange> Ranges) override;
+
+  void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
+                       SmallVectorImpl<CharSourceRange> &Ranges,
+                       ArrayRef<FixItHint> Hints) override {
+    emitSnippetAndCaret(Loc, Level, Ranges, Hints);
+  }
+
+  void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) override;
+
+  void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
+                          StringRef ModuleName) override;
+
+  void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc,
+                                  StringRef ModuleName) override;
+
+private:
+  SarifResult addLocationToResult(SarifResult Result, FullSourceLoc Loc, PresumedLoc PLoc, ArrayRef<CharSourceRange> Ranges, const Diagnostic &Diag);
+  
+  llvm::StringRef emitFilename(StringRef Filename, const SourceManager &SM);
+
+  void emitSnippetAndCaret(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
+                           SmallVectorImpl<CharSourceRange> &Ranges,
+                           ArrayRef<FixItHint> Hints);
+};
+
+} // end namespace clang
+
+#endif
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to