================
@@ -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