diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h --- a/clang/include/clang/Basic/Diagnostic.h +++ b/clang/include/clang/Basic/Diagnostic.h @@ -16,6 +16,7 @@ #include "clang/Basic/DiagnosticIDs.h" #include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/Sarif.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Specifiers.h" #include "llvm/ADT/ArrayRef.h" @@ -183,6 +184,45 @@ DiagnosticStorage() = default; }; +/// Abstract interface for all errors, warnings, and remarks. This allows us to +/// use the visitor pattern. This is something that will be concretely defined +/// in the short-term, as we experiment, but will be migrated over to TableGen +/// after a few diagnostics have been implemented. +class BasicDiagnostic { +public: + virtual ~BasicDiagnostic() = default; + + /// Emits the diagnostic as Clang's original unstructured text format. + virtual void EmitTraditionalDiagnostic() const = 0; + + /// Emits the diagnostic as SARIF. + virtual void EmitSarifDiagnostic(SarifDocumentWriter &) const = 0; + +protected: + using DiagID = unsigned; + + explicit BasicDiagnostic(DiagID K) : ID(K) {} + + DiagID getID() const { return ID; } + +private: + DiagID ID; +}; + +/// Abstract interface for diagnostic contexts (traditionally: notes). +/// A BasicDiagnostic may have as many different contexts as required to provide +/// users with a complete picture. +class DiagnosticContext { +public: + virtual ~DiagnosticContext() = default; + + /// Emits the diagnostic as an unstructured text note. + virtual void EmitTraditionalNote() const = 0; + + /// Emits the diagnostic as SARIF. + virtual void EmitSarifContext(SarifDocumentWriter &) const = 0; +}; + /// Concrete class used by the front-end to report problems and issues. /// /// This massages the diagnostics (e.g. handling things like "report warnings @@ -295,6 +335,7 @@ DiagnosticConsumer *Client = nullptr; std::unique_ptr Owner; SourceManager *SourceMgr = nullptr; + SarifDocumentWriter *SarifWriter = nullptr; /// Mapping information for diagnostics. /// @@ -979,6 +1020,9 @@ /// Return the value associated with this diagnostic flag. StringRef getFlagValue() const { return FlagValue; } + void setSarifWriter(SarifDocumentWriter *W) { SarifWriter = W; } + SarifDocumentWriter *getSarifWriter() const { return SarifWriter; } + private: // This is private state used by DiagnosticBuilder. We put it here instead of // in DiagnosticBuilder in order to keep DiagnosticBuilder a small lightweight @@ -1343,6 +1387,15 @@ return *this; } + const DiagnosticBuilder &operator<<(const BasicDiagnostic &Diag) const { + assert(isActive() && "Clients must not add to cleared diagnostic!"); + if (SarifDocumentWriter *W = DiagObj->getSarifWriter()) + Diag.EmitSarifDiagnostic(*W); + else + Diag.EmitTraditionalDiagnostic(); + return *this; + } + // It is necessary to limit this to rvalue reference to avoid calling this // function with a bitfield lvalue argument since non-const reference to // bitfield is not allowed. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -38,6 +38,7 @@ #include "clang/Basic/BitmaskEnum.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/DarwinSDKInfo.h" +#include "clang/Basic/Diagnostic.h" #include "clang/Basic/ExpressionTraits.h" #include "clang/Basic/Module.h" #include "clang/Basic/OpenCLOptions.h" @@ -1772,6 +1773,14 @@ return Diag; } + const SemaDiagnosticBuilder &operator<<(const BasicDiagnostic &Diag) const { + if (SarifDocumentWriter *W = S.Diags.getSarifWriter()) + Diag.EmitSarifDiagnostic(*W); + else + Diag.EmitTraditionalDiagnostic(); + return *this; + } + // It is necessary to limit this to rvalue reference to avoid calling this // function with a bitfield lvalue argument since non-const reference to // bitfield is not allowed. @@ -13583,7 +13592,6 @@ PragmaMsStackAction Action, llvm::StringRef StackSlotLabel, AlignPackInfo Value); - } // end namespace clang namespace llvm { diff --git a/clang/unittests/Basic/SarifTest.cpp b/clang/unittests/Basic/SarifTest.cpp --- a/clang/unittests/Basic/SarifTest.cpp +++ b/clang/unittests/Basic/SarifTest.cpp @@ -317,4 +317,20 @@ ASSERT_THAT(Output, ::testing::StrEq(ExpectedOutput)); } +// Check that we can set/get a SarifDocumentWriter from the DiagnosticsEngine +// Note that there doesn't appear to be any tests for the DiagnosticsEngine, so +// we test here. +TEST_F(SarifDocumentWriterTest, checkDiagnosticsEngineSetAndGet) { + ASSERT_FALSE(Diags.getSarifWriter()); + + // GIVEN: + SarifDocumentWriter Writer(SourceMgr); + + // WHEN: the DiagnosticsEngine is set + Diags.setSarifWriter(&Writer); + + // THEN: the DiagnosticsEngine points to the local SarifDocumentWriter + EXPECT_TRUE(Diags.getSarifWriter() == &Writer); +} + } // namespace