================
@@ -7,108 +7,228 @@
 
//===----------------------------------------------------------------------===//
 
 #include "clang/Frontend/SARIFDiagnostic.h"
-#include "clang/Basic/CharInfo.h"
+#include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/DiagnosticOptions.h"
-#include "clang/Basic/FileManager.h"
+#include "clang/Basic/DiagnosticSema.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/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/ConvertUTF.h"
 #include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/ErrorOr.h"
 #include "llvm/Support/Locale.h"
-#include "llvm/Support/Path.h"
-#include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <string>
 
 namespace clang {
 
+// In sarif mode,
+// a diagnostics 'group' have 1 top-level error/warning and several sub-level
+// notes. For example:
+//
+// error: static assertion failed.
+//   note: in instantiation of 'cat::meow'.
+//     note: because concept 'paper_tiger' would be invalid.
+// error: invalid operands to binary expression 'cat::meow' and 'dog::wolf'.
+//   note: candidate function not viable.
+//     note: no known conversion from 'tiger::meooooow' to 'cat::meow'
+//   note: candidate function ignored.
+//     note: constraints not satisfied.
+//   note: ... (candidates)
+//     note: ... (reasons)
+//   note: too many candidates.
+// error: too many errors occured, stopping now.
+
 SARIFDiagnostic::SARIFDiagnostic(raw_ostream &OS, const LangOptions &LangOpts,
                                  DiagnosticOptions &DiagOpts,
                                  SarifDocumentWriter *Writer)
-    : DiagnosticRenderer(LangOpts, DiagOpts), Writer(Writer) {}
+    : DiagnosticRenderer(LangOpts, DiagOpts),
+      Root(Node::Result(), Node::Option{&LangOpts, &DiagOpts},
+           /*Nesting=*/-1), // The root does not represents a diagnostic.
+      Current(&Root), Writer(Writer) {
+  // Don't print 'X warnings and Y errors generated'.
+  DiagOpts.ShowCarets = false;
+}
+
+// helper function
+namespace {
+template <class NodeType, class IterateFuncType, class ApplyFuncType>
+void RecursiveFor(NodeType &&Node, IterateFuncType &&IterateFunc,
+                  ApplyFuncType &&ApplyFunc) {
+  for (auto &&Child : IterateFunc(Node)) {
+    ApplyFunc(*Child);
+    RecursiveFor(*Child, IterateFunc, ApplyFunc);
+  }
+}
+} // namespace
+
+SARIFDiagnostic::~SARIFDiagnostic() {
+  // clang-format off
+  for (auto& TopLevelDiagnosticsPtr : Root.getChildrenPtrs()) { // For each 
top-level error/warnings.
+    unsigned DiagID = TopLevelDiagnosticsPtr->getDiagID();
+    SarifRule Rule = SarifRule::create() // Each top-level error/warning has a 
corresponding Rule.
+      .setRuleId(std::to_string(DiagID))
+      .setDefaultConfiguration(
+        SarifReportingConfiguration::create()
+          .setLevel(
+            TopLevelDiagnosticsPtr->getLevel() == 
DiagnosticsEngine::Level::Note    ? SarifResultLevel::Note :
+            TopLevelDiagnosticsPtr->getLevel() == 
DiagnosticsEngine::Level::Remark  ? SarifResultLevel::Note :
+            TopLevelDiagnosticsPtr->getLevel() == 
DiagnosticsEngine::Level::Warning ? SarifResultLevel::Warning :
+            TopLevelDiagnosticsPtr->getLevel() == 
DiagnosticsEngine::Level::Error   ? SarifResultLevel::Error :
+            TopLevelDiagnosticsPtr->getLevel() == 
DiagnosticsEngine::Level::Fatal   ? SarifResultLevel::Error :
+                                         (assert(false && "Invalid diagnostic 
type"), SarifResultLevel::None)
+          )
+          .setRank(
+            TopLevelDiagnosticsPtr->getLevel() <= 
DiagnosticsEngine::Level::Warning ? 0  :
+            TopLevelDiagnosticsPtr->getLevel() == 
DiagnosticsEngine::Level::Error   ? 50 :
+            TopLevelDiagnosticsPtr->getLevel() == 
DiagnosticsEngine::Level::Fatal   ? 100 :
+                                         (assert(false && "Invalid diagnostic 
type"), 0)
+          )
+      );
+    unsigned RuleIndex = Writer->createRule(Rule); // Write into Writer.
+
+    SarifResult Result = SarifResult::create(RuleIndex)
+      .setDiagnosticMessage(TopLevelDiagnosticsPtr->getDiagnosticMessage())
+      .addLocations(TopLevelDiagnosticsPtr->getLocations())
+      .addRelatedLocations(TopLevelDiagnosticsPtr->getRelatedLocations());
+    RecursiveFor(*TopLevelDiagnosticsPtr, [] (Node& Node) -> auto& { return 
Node.getChildrenPtrs(); }, [&] (Node& Node) { // For each (recursive) 
ChildResults.
+      Result.addRelatedLocations({
+        SarifChildResult::create()
+          .setDiagnosticMessage(Node.getDiagnosticMessage())
+          .addLocations(Node.getLocations())
+          .setNesting(Node.getNesting())
+      });
+      Result.addRelatedLocations(Node.getRelatedLocations());
+    });
+    Writer->appendResult(Result); // Write into Writer
+  }
+  // clang-format on
+}
 
-// FIXME(llvm-project/issues/57323): Refactor Diagnostic classes.
 void SARIFDiagnostic::emitDiagnosticMessage(
     FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level,
     StringRef Message, ArrayRef<clang::CharSourceRange> Ranges,
-    DiagOrStoredDiag D) {
+    DiagOrStoredDiag Diag) {
+
+  if (Level >= DiagnosticsEngine::Level::Warning) {
+    Current =
+        &Root; // If this is a top-level error/warning, repoint Current to 
Root.
+  } else {
+    if (Message.starts_with("candidate"))
----------------
Sirraide wrote:

> Actually we should use
> 
> ```c++
> if (Diag.dyn_cast<const Diagnostic*>()->getID() == 
> diag::err_ovl_no_conversion_in_cast || 
>     Diag.dyn_cast<const Diagnostic*>()->getID() == 
> diag::err_ovl_ambiguous_conversion_in_cast || 
>     Diag.dyn_cast<const Diagnostic*>()->getID() == diag::/*all enums that is 
> 'forkable'*/)
> ```

So I’m not sure it’s a good idea to tie it to the diagnostic ID either, but 
*if* we wanted to do it that way somehow, then it should probably be specified 
in TableGen where we define the diagnostic.


> (At the same time, your #151234 seems very attractive! I love this idea and 
> if PR #151234 is completed then the `clang` diagnostic will be much more 
> pretty and fancy.)

Yeah, I need to think about that PR and experiment w/ it a bit more. To 
elaborate, I’m currently leaning towards it being a better idea to actually 
just build an entire diagnostic + notes + nested diagnostics and then handing 
it to the diagnostic engine rather than just emitting individual notes etc. w/ 
a nesting level and relying on the `DiagnosticsEngine` to reassemble the tree 
somehow. This is something someone from the Flang team suggested, and I think 
we’d end up w/ something along the lines of
```
Diag(Loc1, diag::foo)
    .AddNested(Loc2, diag::bar)
```
and while this *does* mean refactoring every place in the code base that cares 
about nesting... I think we’d have to do that anyway to some extent (because we 
might want to change the order in which some of the diagnostics are emitted 
once we support e.g. nesting errors within other errors), and this approach is 
*much* simpler than somehow reconstructing a diagnostics tree based on nesting 
level (and believe me, I’ve tried). Also, nesting notes in errors/warnings can 
still be done automatically so we don’t have to update *every* place where we 
emit notes.

But yeah, I need to get back to this at some point and experiment w/ this 
approach; that might also require its own RFC though.

https://github.com/llvm/llvm-project/pull/174106
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to