diff --git a/clang/include/clang/Basic/DiagnosticOptions.h b/clang/include/clang/Basic/DiagnosticOptions.h --- a/clang/include/clang/Basic/DiagnosticOptions.h +++ b/clang/include/clang/Basic/DiagnosticOptions.h @@ -74,7 +74,7 @@ friend class CompilerInvocation; public: - enum TextDiagnosticFormat { Clang, MSVC, Vi, SARIF }; + enum TextDiagnosticFormat { Clang, MSVC, Vi, SARIFStderr, SARIFFile }; // Default values. enum { diff --git a/clang/include/clang/Basic/DiagnosticOptions.def b/clang/include/clang/Basic/DiagnosticOptions.def --- a/clang/include/clang/Basic/DiagnosticOptions.def +++ b/clang/include/clang/Basic/DiagnosticOptions.def @@ -63,7 +63,7 @@ VALUE_DIAGOPT(ShowCategories, 2, 0) /// Show categories: 0 -> none, 1 -> Number, /// 2 -> Full Name. -ENUM_DIAGOPT(Format, TextDiagnosticFormat, 2, Clang) /// Format for diagnostics: +ENUM_DIAGOPT(Format, TextDiagnosticFormat, 3, Clang) /// Format for diagnostics: DIAGOPT(ShowColors, 1, 0) /// Show diagnostics with ANSI color sequences. DIAGOPT(UseANSIEscapeCodes, 1, 0) diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -5843,8 +5843,8 @@ def fdiagnostics_format : Separate<["-"], "fdiagnostics-format">, HelpText<"Change diagnostic formatting to match IDE and command line tools">, - Values<"clang,msvc,vi,sarif,SARIF">, - NormalizedValuesScope<"DiagnosticOptions">, NormalizedValues<["Clang", "MSVC", "Vi", "SARIF", "SARIF"]>, + Values<"clang,msvc,vi,sarif-stderr,sarif-file">, + NormalizedValuesScope<"DiagnosticOptions">, NormalizedValues<["Clang", "MSVC", "Vi", "SARIFStderr", "SARIFFile"]>, MarshallingInfoEnum, "Clang">; def fdiagnostics_show_category : Separate<["-"], "fdiagnostics-show-category">, HelpText<"Print diagnostic category">, diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -606,8 +606,12 @@ /// /// \param ShouldOwnClient If Client is non-NULL, specifies whether /// the diagnostic object should take ownership of the client. + /// + /// \param ToFile Determines if Clang should write diagnostics to a file. void createDiagnostics(DiagnosticConsumer *Client = nullptr, - bool ShouldOwnClient = true); + bool ShouldOwnClient = true, bool ToFile = false); + + std::string MakeInputPath() const; /// Create a DiagnosticsEngine object with a the TextDiagnosticPrinter. /// @@ -626,12 +630,16 @@ /// \param CodeGenOpts If non-NULL, the code gen options in use, which may be /// used by some diagnostics printers (for logging purposes only). /// + /// \param TargetPath Path to the target that Clang is currently producing. + /// This will be used as the prefix of any file that Clang may write + /// diagnostics to (e.g. `-fdiagnostics-format=sarif-file -o /tmp/hello` would + /// lead to there being a file called `/tmp/hello.sarif`). + /// /// \return The new object on success, or null on failure. - static IntrusiveRefCntPtr - createDiagnostics(DiagnosticOptions *Opts, - DiagnosticConsumer *Client = nullptr, - bool ShouldOwnClient = true, - const CodeGenOptions *CodeGenOpts = nullptr); + static IntrusiveRefCntPtr createDiagnostics( + DiagnosticOptions *Opts, DiagnosticConsumer *Client = nullptr, + bool ShouldOwnClient = true, const CodeGenOptions *CodeGenOpts = nullptr, + StringRef TargetPath = ""); /// Create the file manager and replace any existing one with it. /// diff --git a/clang/include/clang/Frontend/SARIFDiagnosticPrinter.h b/clang/include/clang/Frontend/SARIFDiagnosticPrinter.h --- a/clang/include/clang/Frontend/SARIFDiagnosticPrinter.h +++ b/clang/include/clang/Frontend/SARIFDiagnosticPrinter.h @@ -20,6 +20,8 @@ #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/StringRef.h" #include +#include +#include namespace clang { class DiagnosticOptions; @@ -30,6 +32,12 @@ class SARIFDiagnosticPrinter : public DiagnosticConsumer { public: SARIFDiagnosticPrinter(raw_ostream &OS, DiagnosticOptions *Diags); + + /// Constructs a SARIFDiagnosticPrinter so that it writes to a file mimicing + /// Clang's current output target. The diagnostics file target has the same + /// name suffixed with `.sarif`. + SARIFDiagnosticPrinter(StringRef TargetPath, DiagnosticOptions *Diags); + ~SARIFDiagnosticPrinter() = default; SARIFDiagnosticPrinter &operator=(const SARIFDiagnosticPrinter &&) = delete; @@ -59,7 +67,8 @@ const Diagnostic &Info) override; private: - raw_ostream &OS; + std::optional EC; + std::variant> OS; IntrusiveRefCntPtr DiagOpts; /// Handle to the currently active SARIF diagnostic emitter. @@ -69,6 +78,16 @@ std::string Prefix; std::unique_ptr Writer; + + raw_ostream &getOS() { + // Deliberately not using std::visit due to there being exactly two known + // types. + if (std::holds_alternative(OS)) { + return *std::get(OS); + } else { + return *std::get>(OS); + } + } }; } // end namespace clang diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4032,8 +4032,7 @@ if (const Arg *A = Args.getLastArg(options::OPT_fdiagnostics_format_EQ)) { CmdArgs.push_back("-fdiagnostics-format"); CmdArgs.push_back(A->getValue()); - if (StringRef(A->getValue()) == "sarif" || - StringRef(A->getValue()) == "SARIF") + if (StringRef(A->getValue()).starts_with("sarif")) D.Diag(diag::warn_drv_sarif_format_unstable); } diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -333,16 +333,18 @@ } void CompilerInstance::createDiagnostics(DiagnosticConsumer *Client, - bool ShouldOwnClient) { - Diagnostics = createDiagnostics(&getDiagnosticOpts(), Client, - ShouldOwnClient, &getCodeGenOpts()); -} - -IntrusiveRefCntPtr -CompilerInstance::createDiagnostics(DiagnosticOptions *Opts, - DiagnosticConsumer *Client, - bool ShouldOwnClient, - const CodeGenOptions *CodeGenOpts) { + bool ShouldOwnClient, bool ToFile) { + StringRef Input = + llvm::sys::path::filename(getFrontendOpts().Inputs.front().getFile()); + StringRef Output = getFrontendOpts().OutputFile; + Diagnostics = + createDiagnostics(&getDiagnosticOpts(), Client, ShouldOwnClient, + &getCodeGenOpts(), Output.empty() ? Input : Output); +} + +IntrusiveRefCntPtr CompilerInstance::createDiagnostics( + DiagnosticOptions *Opts, DiagnosticConsumer *Client, bool ShouldOwnClient, + const CodeGenOptions *CodeGenOpts, StringRef TargetPath) { IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); IntrusiveRefCntPtr Diags(new DiagnosticsEngine(DiagID, Opts)); @@ -351,8 +353,10 @@ // implementing -verify. if (Client) { Diags->setClient(Client, ShouldOwnClient); - } else if (Opts->getFormat() == DiagnosticOptions::SARIF) { + } else if (Opts->getFormat() == DiagnosticOptions::SARIFStderr) { Diags->setClient(new SARIFDiagnosticPrinter(llvm::errs(), Opts)); + } else if (Opts->getFormat() == DiagnosticOptions::SARIFFile) { + Diags->setClient(new SARIFDiagnosticPrinter(TargetPath, Opts)); } else Diags->setClient(new TextDiagnosticPrinter(llvm::errs(), Opts)); @@ -1221,7 +1225,6 @@ &ImportingInstance.getModuleCache()); auto &Inv = *Invocation; Instance.setInvocation(std::move(Invocation)); - Instance.createDiagnostics(new ForwardingDiagnosticConsumer( ImportingInstance.getDiagnosticClient()), /*ShouldOwnClient=*/true); diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -724,7 +724,9 @@ } if (!CI.hasSourceManager()) { CI.createSourceManager(CI.getFileManager()); - if (CI.getDiagnosticOpts().getFormat() == DiagnosticOptions::SARIF) { + if (TextDiagnosticFormat Format = CI.getDiagnosticOpts().getFormat(); + Format == DiagnosticOptions::SARIFStderr || + Format == DiagnosticOptions::SARIFFile) { static_cast(&CI.getDiagnosticClient()) ->setSarifWriter( std::make_unique(CI.getSourceManager())); diff --git a/clang/lib/Frontend/SARIFDiagnosticPrinter.cpp b/clang/lib/Frontend/SARIFDiagnosticPrinter.cpp --- a/clang/lib/Frontend/SARIFDiagnosticPrinter.cpp +++ b/clang/lib/Frontend/SARIFDiagnosticPrinter.cpp @@ -22,19 +22,37 @@ #include "llvm/Support/JSON.h" #include "llvm/Support/raw_ostream.h" #include +#include namespace clang { SARIFDiagnosticPrinter::SARIFDiagnosticPrinter(raw_ostream &OS, DiagnosticOptions *Diags) - : OS(OS), DiagOpts(Diags) {} + : OS(&OS), DiagOpts(Diags) {} + +static std::unique_ptr +OpenDiagnosticFile(StringRef TargetPath, std::error_code &EC) { + assert(!TargetPath.empty()); + return std::make_unique( + std::string(TargetPath) + ".sarif", EC); +} + +SARIFDiagnosticPrinter::SARIFDiagnosticPrinter(StringRef TargetPath, + DiagnosticOptions *Diags) + : EC(std::error_code()), OS(OpenDiagnosticFile(TargetPath, *EC)), + DiagOpts(Diags) { + if (*EC) { + assert(false and "not implemented yet: not even sure what to do"); + } +} void SARIFDiagnosticPrinter::BeginSourceFile(const LangOptions &LO, const Preprocessor *PP) { // Build the SARIFDiagnostic utility. assert(hasSarifWriter() && "Writer not set!"); assert(!SARIFDiag && "SARIFDiagnostic already set."); - SARIFDiag = std::make_unique(OS, LO, &*DiagOpts, &*Writer); + SARIFDiag = + std::make_unique(getOS(), LO, &*DiagOpts, &*Writer); // Initialize the SARIF object. Writer->createRun("clang", Prefix); } @@ -43,8 +61,13 @@ assert(SARIFDiag && "SARIFDiagnostic has not been set."); Writer->endRun(); llvm::json::Value Value(Writer->createDocument()); - OS << "\n" << Value << "\n\n"; - OS.flush(); + getOS() << '\n' << Value << '\n'; + getOS().flush(); + if (EC and *EC) { + llvm::errs() << "error: " << EC->message() << '\n'; + llvm::errs() << "outputting to stderr as a backup\n"; + llvm::errs() << Value << '\n'; + } SARIFDiag.reset(); } diff --git a/clang/lib/Frontend/TextDiagnostic.cpp b/clang/lib/Frontend/TextDiagnostic.cpp --- a/clang/lib/Frontend/TextDiagnostic.cpp +++ b/clang/lib/Frontend/TextDiagnostic.cpp @@ -815,7 +815,8 @@ emitFilename(PLoc.getFilename(), Loc.getManager()); switch (DiagOpts->getFormat()) { - case DiagnosticOptions::SARIF: + case DiagnosticOptions::SARIFStderr: + case DiagnosticOptions::SARIFFile: case DiagnosticOptions::Clang: if (DiagOpts->ShowLine) OS << ':' << LineNo; @@ -838,7 +839,8 @@ OS << ColNo; } switch (DiagOpts->getFormat()) { - case DiagnosticOptions::SARIF: + case DiagnosticOptions::SARIFStderr: + case DiagnosticOptions::SARIFFile: case DiagnosticOptions::Clang: case DiagnosticOptions::Vi: OS << ':'; break; case DiagnosticOptions::MSVC: diff --git a/clang/lib/Tooling/Tooling.cpp b/clang/lib/Tooling/Tooling.cpp --- a/clang/lib/Tooling/Tooling.cpp +++ b/clang/lib/Tooling/Tooling.cpp @@ -425,7 +425,9 @@ std::unique_ptr ScopedToolAction(create()); // Create the compiler's actual diagnostics engine. - Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false); + Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false, + Invocation->DiagnosticOpts->getFormat() == + DiagnosticOptions::SARIFFile); if (!Compiler.hasDiagnostics()) return false; @@ -684,7 +686,7 @@ if (!Invocation.run()) return nullptr; - + assert(ASTs.size() == 1); return std::move(ASTs[0]); } diff --git a/clang/test/Driver/fdiagnostics-format-sarif.cpp b/clang/test/Driver/fdiagnostics-format-sarif.cpp --- a/clang/test/Driver/fdiagnostics-format-sarif.cpp +++ b/clang/test/Driver/fdiagnostics-format-sarif.cpp @@ -1,5 +1,5 @@ -// RUN: %clang -fsyntax-only -fdiagnostics-format=sarif %s -### 2>&1 | FileCheck %s --check-prefix=WARN +// RUN: %clang -fsyntax-only -fdiagnostics-format=sarif-stderr %s -### 2>&1 | FileCheck %s --check-prefix=WARN // WARN: warning: diagnostic formatting in SARIF mode is currently unstable [-Wsarif-format-unstable] -// RUN: %clang -fsyntax-only -fdiagnostics-format=SARIF %s -### 2>&1 | FileCheck %s --check-prefix=WARN2 +// RUN: %clang -fsyntax-only -fdiagnostics-format=sarif-file %s -### 2>&1 | FileCheck %s --check-prefix=WARN2 // WARN2: warning: diagnostic formatting in SARIF mode is currently unstable [-Wsarif-format-unstable] diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp --- a/clang/tools/driver/cc1_main.cpp +++ b/clang/tools/driver/cc1_main.cpp @@ -230,7 +230,12 @@ CompilerInvocation::GetResourcesPath(Argv0, MainAddr); // Create the actual diagnostics engine. - Clang->createDiagnostics(); + auto diagnostics_format_option = + llvm::find(Argv, StringRef("-fdiagnostics-format")); + bool IsSarifFile = + std::distance(diagnostics_format_option, Argv.end()) > 1 && + *std::next(diagnostics_format_option) == StringRef("sarif-file"); + Clang->createDiagnostics(nullptr, true, IsSarifFile); if (!Clang->hasDiagnostics()) return 1;