cjdb created this revision. cjdb added reviewers: aaron.ballman, erichkeane, shafik. Herald added subscribers: carlosgalvezp, kadircet, arphaman. Herald added a reviewer: njames93. Herald added projects: Flang, All. cjdb requested review of this revision. Herald added subscribers: cfe-commits, libcxx-commits, lldb-commits, jdoerfert. Herald added projects: clang, LLDB, libc++abi, clang-tools-extra. Herald added a reviewer: libc++abi.
Part of the [revised diagnostic model][1] is to provide users with diagnostics that explain what's going wrong from their perspective, as opposed to from the compiler's perspective. This may be achieved through a rewording of the reason, or by provididng more info, or both. This commit also changes the diagnostic for an explicit template parameter gone wrong to demonstrate how it works. NOTE: this is a work-in-progress. Most of the work has been fleshed out, but the note needs to be provided with the kind of template argument it is given. I'm looking into doing this, but it seems to require a lot of tedious manual changes, and I'd prefer to get input on the changes that are currently in the diff before making those extra changes (which will distract from the current stuff, and may ultimately be unnecessary if the current direction is undesirable). [1]: https://discourse.llvm.org/t/rfc-improving-clang-s-diagnostics/62584 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D138939 Files: clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp clang-tools-extra/clangd/Compiler.cpp clang-tools-extra/clangd/Diagnostics.cpp clang/include/clang/Basic/Diagnostic.h clang/include/clang/Basic/Diagnostic.td clang/include/clang/Basic/DiagnosticAST.h clang/include/clang/Basic/DiagnosticAnalysis.h clang/include/clang/Basic/DiagnosticComment.h clang/include/clang/Basic/DiagnosticCrossTU.h clang/include/clang/Basic/DiagnosticDriver.h clang/include/clang/Basic/DiagnosticFrontend.h clang/include/clang/Basic/DiagnosticIDs.h clang/include/clang/Basic/DiagnosticLex.h clang/include/clang/Basic/DiagnosticParse.h clang/include/clang/Basic/DiagnosticRefactoring.h clang/include/clang/Basic/DiagnosticSema.h clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Basic/DiagnosticSerialization.h clang/include/clang/Basic/PartialDiagnostic.h clang/include/clang/Frontend/ASTUnit.h clang/include/clang/Frontend/DiagnosticRenderer.h clang/lib/Basic/Diagnostic.cpp clang/lib/Basic/DiagnosticIDs.cpp clang/lib/Format/TokenAnalyzer.cpp clang/lib/Frontend/ASTUnit.cpp clang/lib/Frontend/LogDiagnosticPrinter.cpp clang/lib/Frontend/SARIFDiagnosticPrinter.cpp clang/lib/Frontend/SerializedDiagnosticPrinter.cpp clang/lib/Frontend/TextDiagnosticBuffer.cpp clang/lib/Frontend/TextDiagnosticPrinter.cpp clang/lib/Sema/SemaOverload.cpp clang/test/Frontend/sarif-reason.cpp clang/test/TableGen/DiagnosticBase.inc clang/test/TableGen/deferred-diag.td clang/tools/clang-format/ClangFormat.cpp clang/tools/clang-import-test/clang-import-test.cpp clang/tools/diagtool/DiagnosticNames.cpp clang/unittests/Driver/SimpleDiagnosticConsumer.h clang/unittests/Driver/ToolChainTest.cpp clang/unittests/Frontend/FrontendActionTest.cpp clang/unittests/Tooling/RewriterTestContext.h clang/utils/TableGen/ClangDiagnosticsEmitter.cpp flang/lib/Frontend/TextDiagnosticBuffer.cpp flang/lib/Frontend/TextDiagnosticPrinter.cpp libcxxabi/test/test_demangle.pass.cpp lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
Index: lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp =================================================================== --- lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -697,7 +697,8 @@ const clang::Diagnostic &info) override { if (m_log) { llvm::SmallVector<char, 32> diag_str(10); - info.FormatDiagnostic(diag_str); + info.FormatSummary(diag_str); + info.FormatLegacyReason(diag_str); diag_str.push_back('\0'); LLDB_LOGF(m_log, "Compiler diagnostic: %s\n", diag_str.data()); } Index: lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -192,7 +192,8 @@ Log *log = GetLog(LLDBLog::Expressions); if (log) { llvm::SmallVector<char, 32> diag_str; - Info.FormatDiagnostic(diag_str); + Info.FormatSummary(diag_str); + Info.FormatLegacyReason(diag_str); diag_str.push_back('\0'); const char *plain_diag = diag_str.data(); LLDB_LOG(log, "Received diagnostic outside parsing: {0}", plain_diag); Index: flang/lib/Frontend/TextDiagnosticPrinter.cpp =================================================================== --- flang/lib/Frontend/TextDiagnosticPrinter.cpp +++ flang/lib/Frontend/TextDiagnosticPrinter.cpp @@ -37,7 +37,8 @@ // Render the diagnostic message into a temporary buffer eagerly. We'll use // this later as we print out the diagnostic to the terminal. llvm::SmallString<100> outStr; - info.FormatDiagnostic(outStr); + info.FormatSummary(outStr); + info.FormatLegacyReason(outStr); llvm::raw_svector_ostream diagMessageStream(outStr); Index: flang/lib/Frontend/TextDiagnosticBuffer.cpp =================================================================== --- flang/lib/Frontend/TextDiagnosticBuffer.cpp +++ flang/lib/Frontend/TextDiagnosticBuffer.cpp @@ -29,7 +29,8 @@ DiagnosticConsumer::HandleDiagnostic(level, info); llvm::SmallString<100> buf; - info.FormatDiagnostic(buf); + info.FormatSummary(buf); + info.FormatLegacyReason(buf); switch (level) { default: llvm_unreachable("Diagnostic not handled during diagnostic buffering!"); Index: clang/utils/TableGen/ClangDiagnosticsEmitter.cpp =================================================================== --- clang/utils/TableGen/ClangDiagnosticsEmitter.cpp +++ clang/utils/TableGen/ClangDiagnosticsEmitter.cpp @@ -604,7 +604,7 @@ std::vector<std::string> buildForDocumentation(StringRef Role, const Record *R); - std::string buildForDefinition(const Record *R); + std::string buildForDefinition(const Record *R, StringRef Field); Piece *getSubstitution(SubstitutionPiece *S) const { auto It = Substitutions.find(S->Name); @@ -1182,9 +1182,10 @@ return Result; } -std::string DiagnosticTextBuilder::buildForDefinition(const Record *R) { +std::string DiagnosticTextBuilder::buildForDefinition(const Record *R, + StringRef Field) { EvaluatingRecordGuard Guard(&EvaluatingRecord, R); - StringRef Text = R->getValueAsString("Summary"); + StringRef Text = R->getValueAsString(Field); DiagText D(*this, Text); std::string Result; DiagTextPrinter{*this, Result}.Visit(D.Root); @@ -1276,7 +1277,7 @@ // Description string. OS << ", \""; - OS.write_escaped(DiagTextBuilder.buildForDefinition(&R)) << '"'; + OS.write_escaped(DiagTextBuilder.buildForDefinition(&R, "Summary")) << '"'; // Warning group associated with the diagnostic. This is stored as an index // into the alphabetically sorted warning group table. @@ -1320,6 +1321,17 @@ // Category number. OS << ", " << CategoryIDs.getID(getDiagnosticCategory(&R, DGParentMap)); + + // Reasons + OS << ", /*Reason.Legacy=*/\""; + OS.write_escaped( + DiagTextBuilder.buildForDefinition(R.getValueAsDef("Reason"), "Legacy")) + << '"'; + + OS << ", /*Reason.SARIF=*/\""; + OS.write_escaped( + DiagTextBuilder.buildForDefinition(R.getValueAsDef("Reason"), "SARIF")) + << '"'; OS << ")\n"; } } Index: clang/unittests/Tooling/RewriterTestContext.h =================================================================== --- clang/unittests/Tooling/RewriterTestContext.h +++ clang/unittests/Tooling/RewriterTestContext.h @@ -35,7 +35,8 @@ const Diagnostic &Info) override { ++NumDiagnosticsSeen; SmallString<100> OutStr; - Info.FormatDiagnostic(OutStr); + Info.FormatSummary(OutStr); + Info.FormatLegacyReason(OutStr); llvm::errs() << OutStr; } unsigned NumDiagnosticsSeen; Index: clang/unittests/Frontend/FrontendActionTest.cpp =================================================================== --- clang/unittests/Frontend/FrontendActionTest.cpp +++ clang/unittests/Frontend/FrontendActionTest.cpp @@ -221,10 +221,12 @@ // Capture errors and notes. There should be one of each. if (DiagLevel == DiagnosticsEngine::Error) { assert(Error.empty()); - Info.FormatDiagnostic(Error); + Info.FormatSummary(Error); + Info.FormatLegacyReason(Error); } else { assert(Note.empty()); - Info.FormatDiagnostic(Note); + Info.FormatSummary(Note); + Info.FormatLegacyReason(Note); } } SmallString<32> Error; Index: clang/unittests/Driver/ToolChainTest.cpp =================================================================== --- clang/unittests/Driver/ToolChainTest.cpp +++ clang/unittests/Driver/ToolChainTest.cpp @@ -378,10 +378,12 @@ const Diagnostic &Info) override { if (DiagLevel == DiagnosticsEngine::Level::Error) { Errors.emplace_back(); - Info.FormatDiagnostic(Errors.back()); + Info.FormatSummary(Errors.back()); + Info.FormatLegacyReason(Errors.back()); } else { Msgs.emplace_back(); - Info.FormatDiagnostic(Msgs.back()); + Info.FormatSummary(Msgs.back()); + Info.FormatLegacyReason(Msgs.back()); } } void clear() override { Index: clang/unittests/Driver/SimpleDiagnosticConsumer.h =================================================================== --- clang/unittests/Driver/SimpleDiagnosticConsumer.h +++ clang/unittests/Driver/SimpleDiagnosticConsumer.h @@ -21,10 +21,12 @@ const clang::Diagnostic &Info) override { if (DiagLevel == clang::DiagnosticsEngine::Level::Error) { Errors.emplace_back(); - Info.FormatDiagnostic(Errors.back()); + Info.FormatSummary(Errors.back()); + Info.FormatLegacyReason(Errors.back()); } else { Msgs.emplace_back(); - Info.FormatDiagnostic(Msgs.back()); + Info.FormatSummary(Msgs.back()); + Info.FormatLegacyReason(Msgs.back()); } } void clear() override { Index: clang/tools/diagtool/DiagnosticNames.cpp =================================================================== --- clang/tools/diagtool/DiagnosticNames.cpp +++ clang/tools/diagtool/DiagnosticNames.cpp @@ -28,7 +28,8 @@ // out of sync easily? static const DiagnosticRecord BuiltinDiagnosticsByID[] = { #define DIAG(ENUM, CLASS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFER, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFER, CATEGORY, LEGACY_REASON, \ + SARIF_REASON) \ {#ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t)}, #include "clang/Basic/DiagnosticCommonKinds.inc" #include "clang/Basic/DiagnosticCrossTUKinds.inc" Index: clang/tools/clang-import-test/clang-import-test.cpp =================================================================== --- clang/tools/clang-import-test/clang-import-test.cpp +++ clang/tools/clang-import-test/clang-import-test.cpp @@ -141,7 +141,8 @@ } SmallString<16> DiagText; - Info.FormatDiagnostic(DiagText); + Info.FormatSummary(DiagText); + Info.FormatLegacyReason(DiagText); llvm::errs() << DiagText << '\n'; if (Info.getLocation().isValid()) { Index: clang/tools/clang-format/ClangFormat.cpp =================================================================== --- clang/tools/clang-format/ClangFormat.cpp +++ clang/tools/clang-format/ClangFormat.cpp @@ -393,7 +393,8 @@ const Diagnostic &Info) override { SmallVector<char, 16> vec; - Info.FormatDiagnostic(vec); + Info.FormatSummary(vec); + Info.FormatLegacyReason(vec); errs() << "clang-format error:" << vec << "\n"; } }; Index: clang/test/TableGen/deferred-diag.td =================================================================== --- clang/test/TableGen/deferred-diag.td +++ clang/test/TableGen/deferred-diag.td @@ -5,23 +5,23 @@ // Test usage of Deferrable and NonDeferrable in diagnostics. def test_default : Error<"This error is non-deferrable by default">; -// CHECK-DAG: DIAG(test_default, {{.*}}SFINAE_SubstitutionFailure, false, true, true, false, 0) +// CHECK-DAG: DIAG(test_default, {{.*}}SFINAE_SubstitutionFailure, false, true, true, false, 0, /*Reason.Legacy=*/"", /*Reason.SARIF=*/"") def test_deferrable : Error<"This error is deferrable">, Deferrable; -// CHECK-DAG: DIAG(test_deferrable, {{.*}} SFINAE_SubstitutionFailure, false, true, true, true, 0) +// CHECK-DAG: DIAG(test_deferrable, {{.*}} SFINAE_SubstitutionFailure, false, true, true, true, 0, /*Reason.Legacy=*/"", /*Reason.SARIF=*/"") def test_non_deferrable : Error<"This error is non-deferrable">, NonDeferrable; -// CHECK-DAG: DIAG(test_non_deferrable, {{.*}} SFINAE_SubstitutionFailure, false, true, true, false, 0) +// CHECK-DAG: DIAG(test_non_deferrable, {{.*}} SFINAE_SubstitutionFailure, false, true, true, false, 0, /*Reason.Legacy=*/"", /*Reason.SARIF=*/"") let Deferrable = 1 in { def test_let : Error<"This error is deferrable by let">; -// CHECK-DAG: DIAG(test_let, {{.*}} SFINAE_SubstitutionFailure, false, true, true, true, 0) +// CHECK-DAG: DIAG(test_let, {{.*}} SFINAE_SubstitutionFailure, false, true, true, true, 0, /*Reason.Legacy=*/"", /*Reason.SARIF=*/"") // Make sure TextSubstitution is allowed in the let Deferrable block. def textsub : TextSubstitution<"%select{text1|text2}0">; def test_let2 : Error<"This error is deferrable by let %sub{textsub}0">; -// CHECK-DAG: DIAG(test_let2, {{.*}} SFINAE_SubstitutionFailure, false, true, true, true, 0) +// CHECK-DAG: DIAG(test_let2, {{.*}} SFINAE_SubstitutionFailure, false, true, true, true, 0, /*Reason.Legacy=*/"", /*Reason.SARIF=*/"") } Index: clang/test/TableGen/DiagnosticBase.inc =================================================================== --- clang/test/TableGen/DiagnosticBase.inc +++ clang/test/TableGen/DiagnosticBase.inc @@ -64,10 +64,26 @@ class InGroup<DiagGroup G> { DiagGroup Group = G; } //class IsGroup<string Name> { DiagGroup Group = DiagGroup<Name>; } +class DiagReason<string legacy, string structured = legacy> { + // A legacy reason is a reason that is part of Clang's legacy diagnostic model, + // but needed to be split from the summary in order to facilitate the existence + // of the structured diagnostic (which will have very different text). One may + // observe that older versions of Clang's diagnostics are the same as + // `Diagnostic.Summary + Diagnostic.Reason.Legacy`. This is intentional, so as + // to preserve the legacy diagnostic model for the time being. + string Legacy = legacy; + + // The SARIF reason will appear when Clang is asked to emit SARIF diagnostics, + // in place of Legacy. Unlike Legacy, which is a fragment of the original + // diagnostic from past-Clang, SARIF reasons may have completely different text + // to explain the problem, often from the perspective of a user. + string SARIF = structured; +} +def NO_REASON_YET : DiagReason<"">; include "DiagnosticDocs.inc" // All diagnostics emitted by the compiler are an indirect subclass of this. -class Diagnostic<string summary, DiagClass DC, Severity defaultmapping> { +class Diagnostic<string summary, DiagClass DC, Severity defaultmapping, DiagReason reason> { /// Component is specified by the file with a big let directive. string Component = ?; string Summary = summary; @@ -81,6 +97,7 @@ Severity DefaultSeverity = defaultmapping; DiagGroup Group; string CategoryName = ""; + DiagReason Reason = reason; } class SFINAEFailure { @@ -118,24 +135,24 @@ } // FIXME: ExtWarn and Extension should also be SFINAEFailure by default. -class Error<string str> : Diagnostic<str, CLASS_ERROR, SEV_Error>, SFINAEFailure { +class Error<string str, DiagReason reason = NO_REASON_YET> : Diagnostic<str, CLASS_ERROR, SEV_Error, reason>, SFINAEFailure { bit ShowInSystemHeader = 1; } // Warnings default to on (but can be default-off'd with DefaultIgnore). // This is used for warnings about questionable code; warnings about // accepted language extensions should use Extension or ExtWarn below instead. -class Warning<string str> : Diagnostic<str, CLASS_WARNING, SEV_Warning>; +class Warning<string str, DiagReason reason = NO_REASON_YET> : Diagnostic<str, CLASS_WARNING, SEV_Warning, reason>; // Remarks can be turned on with -R flags and provide commentary, e.g. on // optimizer decisions. -class Remark<string str> : Diagnostic<str, CLASS_REMARK, SEV_Ignored>; +class Remark<string str, DiagReason reason = NO_REASON_YET> : Diagnostic<str, CLASS_REMARK, SEV_Ignored, reason>; // Extensions are warnings about accepted language extensions. // Extension warnings are default-off but enabled by -pedantic. -class Extension<string str> : Diagnostic<str, CLASS_EXTENSION, SEV_Ignored>; +class Extension<string str, DiagReason reason = NO_REASON_YET> : Diagnostic<str, CLASS_EXTENSION, SEV_Ignored, reason>; // ExtWarns are warnings about accepted language extensions. // ExtWarn warnings are default-on. -class ExtWarn<string str> : Diagnostic<str, CLASS_EXTENSION, SEV_Warning>; +class ExtWarn<string str, DiagReason reason = NO_REASON_YET> : Diagnostic<str, CLASS_EXTENSION, SEV_Warning, reason>; // Notes can provide supplementary information on errors, warnings, and remarks. -class Note<string str> : Diagnostic<str, CLASS_NOTE, SEV_Fatal/*ignored*/>; +class Note<string str, DiagReason reason = NO_REASON_YET> : Diagnostic<str, CLASS_NOTE, SEV_Fatal/*ignored*/, reason>; class DefaultIgnore { Severity DefaultSeverity = SEV_Ignored; } Index: clang/test/Frontend/sarif-reason.cpp =================================================================== --- /dev/null +++ clang/test/Frontend/sarif-reason.cpp @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only +// RUN: %clang -fsyntax-only -fdiagnostics-format=sarif %s > %t 2>&1 +// RUN: FileCheck -dump-input=always %s --input-file=%t --check-prefix=EXPECTED-ERROR --check-prefix=EXPECTED-NOTE + +template<class T> +void f1(); // expected-note 2 {{candidate template ignored: invalid explicitly-specified argument for template parameter 'T'}} +template<int T> +void f2(); // expected-note 2 {{candidate template ignored: invalid explicitly-specified argument for template parameter 'T'}} +template<template<class> class T> +void f3(); // expected-note 2 {{candidate template ignored: invalid explicitly-specified argument for template parameter 'T'}} + +template<class> struct S; + +void g() { + f1<0>(); // expected-error{{no matching function for call to 'f1'}} + f1<S>(); // expected-error{{no matching function for call to 'f1'}} + + f2<int>(); // expected-error{{no matching function for call to 'f2'}} + f2<S>(); // expected-error{{no matching function for call to 'f2'}} + + f3<int>(); // expected-error{{no matching function for call to 'f3'}} + f3<0>(); // expected-error{{no matching function for call to 'f3'}} +} + +// EXPECTED-ERROR: no matching function for call to 'f1' +// EXPECTED-NOTE: candidate template ignored: we passed a value as our first explicit template parameter, but this candidate expects a type for 'T' + +// EXPECTED-ERROR: no matching function for call to 'f1' +// EXPECTED-NOTE: candidate template ignored: we passed a class template as our first explicit template parameter, but this candidate expects a type for 'T' + +// EXPECTED-ERROR: no matching function for call to 'f2' +// EXPECTED-NOTE: candidate template ignored: we passed a type as our first explicit template parameter, but this candidate expects a value for 'T' + +// EXPECTED-ERROR: no matching function for call to 'f2' +// EXPECTED-NOTE: candidate template ignored: we passed a class template as our first explicit template parameter, but this candidate expects a value for 'T' + +// EXPECTED-ERROR: no matching function for call to 'f3' +// EXPECTED-NOTE: candidate template ignored: we passed a type as our first explicit template parameter, but this candidate expects a class template for 'T' + +// EXPECTED-ERROR: no matching function for call to 'f3' +// EXPECTED-NOTE: candidate template ignored: we passed a value as our first explicit template parameter, but this candidate expects a class template for 'T' Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -6511,7 +6511,7 @@ NamedDecl *ND = Function; if (auto *SpecInfo = Function->getTemplateSpecializationInfo()) ND = SpecInfo->getTemplate(); - + if (ND->getFormalLinkage() == Linkage::InternalLinkage) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_module_mismatched; @@ -11108,28 +11108,51 @@ return; } - case Sema::TDK_InvalidExplicitArguments: + case Sema::TDK_InvalidExplicitArguments: { assert(ParamD && "no parameter found for invalid explicit arguments"); + + unsigned index = 1; + int parameter_kind = 0; + if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(ParamD)) { + index += TTP->getIndex(); + parameter_kind = 0; + } else if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(ParamD)) { + index += NTTP->getIndex(); + parameter_kind = 1; + } else { + index += cast<TemplateTemplateParmDecl>(ParamD)->getIndex(); + parameter_kind = 2; + } + + // QualType CallType = dyn_cast<CallExpr>(Fn)->getType(); + // auto *FT = dyn_cast<FunctionTemplateDecl>(CallType); + // TemplateParameterList *Args = FT->getTemplateParameters(); + // assert(Args && "We should have a full list of template arguments at this + // point"); assert(Args->size() >= index && "The argument list should at + // least be as long as the parameter we're indexing"); + + int arg_kind = 0; + // if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(Args->getParam(index - + // 1))) + // arg_kind = 0; + // else if (auto *NTTP = + // dyn_cast<NonTypeTemplateParmDecl>(Args->getParam(index - 1))) + // arg_kind = 1; + // else + // arg_kind = 2; + if (ParamD->getDeclName()) S.Diag(Templated->getLocation(), diag::note_ovl_candidate_explicit_arg_mismatch_named) - << ParamD->getDeclName(); - else { - int index = 0; - if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(ParamD)) - index = TTP->getIndex(); - else if (NonTypeTemplateParmDecl *NTTP - = dyn_cast<NonTypeTemplateParmDecl>(ParamD)) - index = NTTP->getIndex(); - else - index = cast<TemplateTemplateParmDecl>(ParamD)->getIndex(); + << ParamD->getDeclName() << arg_kind << index << parameter_kind; + else S.Diag(Templated->getLocation(), diag::note_ovl_candidate_explicit_arg_mismatch_unnamed) - << (index + 1); - } + << index; + MaybeEmitInheritedConstructorNote(S, Found); return; - + } case Sema::TDK_ConstraintsNotSatisfied: { // Format the template argument list into the argument string. SmallString<128> TemplateArgString; @@ -13943,8 +13966,8 @@ Diag(FnDecl->getLocation(), diag::note_ovl_ambiguous_oper_binary_reversed_self); // Mark member== const or provide matching != to disallow reversed - // args. Eg. - // struct S { bool operator==(const S&); }; + // args. Eg. + // struct S { bool operator==(const S&); }; // S()==S(); if (auto *MD = dyn_cast<CXXMethodDecl>(FnDecl)) if (Op == OverloadedOperatorKind::OO_EqualEqual && Index: clang/lib/Frontend/TextDiagnosticPrinter.cpp =================================================================== --- clang/lib/Frontend/TextDiagnosticPrinter.cpp +++ clang/lib/Frontend/TextDiagnosticPrinter.cpp @@ -114,7 +114,8 @@ // Render the diagnostic message into a temporary buffer eagerly. We'll use // this later as we print out the diagnostic to the terminal. SmallString<100> OutStr; - Info.FormatDiagnostic(OutStr); + Info.FormatSummary(OutStr); + Info.FormatLegacyReason(OutStr); llvm::raw_svector_ostream DiagMessageStream(OutStr); printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts); Index: clang/lib/Frontend/TextDiagnosticBuffer.cpp =================================================================== --- clang/lib/Frontend/TextDiagnosticBuffer.cpp +++ clang/lib/Frontend/TextDiagnosticBuffer.cpp @@ -26,7 +26,8 @@ DiagnosticConsumer::HandleDiagnostic(Level, Info); SmallString<100> Buf; - Info.FormatDiagnostic(Buf); + Info.FormatSummary(Buf); + Info.FormatLegacyReason(Buf); switch (Level) { default: llvm_unreachable( "Diagnostic not handled during diagnostic buffering!"); Index: clang/lib/Frontend/SerializedDiagnosticPrinter.cpp =================================================================== --- clang/lib/Frontend/SerializedDiagnosticPrinter.cpp +++ clang/lib/Frontend/SerializedDiagnosticPrinter.cpp @@ -574,7 +574,8 @@ "Received a diagnostic after we've already started teardown."); if (IsFinishing) { SmallString<256> diagnostic; - Info.FormatDiagnostic(diagnostic); + Info.FormatSummary(diagnostic); + Info.FormatLegacyReason(diagnostic); getMetaDiags()->Report( diag::warn_fe_serialized_diag_failure_during_finalisation) << diagnostic; @@ -594,7 +595,8 @@ // Compute the diagnostic text. State->diagBuf.clear(); - Info.FormatDiagnostic(State->diagBuf); + Info.FormatSummary(State->diagBuf); + Info.FormatLegacyReason(State->diagBuf); if (Info.getLocation().isInvalid()) { // Special-case diagnostics with no location. We may not have entered a Index: clang/lib/Frontend/SARIFDiagnosticPrinter.cpp =================================================================== --- clang/lib/Frontend/SARIFDiagnosticPrinter.cpp +++ clang/lib/Frontend/SARIFDiagnosticPrinter.cpp @@ -58,7 +58,8 @@ // 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); + Info.FormatSummary(OutStr); + Info.FormatSARIFReason(OutStr); llvm::raw_svector_ostream DiagMessageStream(OutStr); Index: clang/lib/Frontend/LogDiagnosticPrinter.cpp =================================================================== --- clang/lib/Frontend/LogDiagnosticPrinter.cpp +++ clang/lib/Frontend/LogDiagnosticPrinter.cpp @@ -133,7 +133,8 @@ // Format the message. SmallString<100> MessageStr; - Info.FormatDiagnostic(MessageStr); + Info.FormatSummary(MessageStr); + Info.FormatLegacyReason(MessageStr); DE.Message = std::string(MessageStr.str()); // Set the location information. @@ -160,4 +161,3 @@ // Record the diagnostic entry. Entries.push_back(DE); } - Index: clang/lib/Frontend/ASTUnit.cpp =================================================================== --- clang/lib/Frontend/ASTUnit.cpp +++ clang/lib/Frontend/ASTUnit.cpp @@ -2382,8 +2382,8 @@ FH.RemoveRange = CharSourceRange::getCharRange(BL, EL); } - Result.push_back(StoredDiagnostic(SD.Level, SD.ID, - SD.Message, Loc, Ranges, FixIts)); + Result.push_back(StoredDiagnostic(SD.Level, SD.ID, SD.Message, SD.Reason, + Loc, Ranges, FixIts)); } Result.swap(Out); } Index: clang/lib/Format/TokenAnalyzer.cpp =================================================================== --- clang/lib/Format/TokenAnalyzer.cpp +++ clang/lib/Format/TokenAnalyzer.cpp @@ -44,7 +44,8 @@ if (DiagLevel == DiagnosticsEngine::Fatal) { Fatal = true; llvm::SmallVector<char, 128> Message; - Info.FormatDiagnostic(Message); + Info.FormatSummary(Message); + Info.FormatLegacyReason(Message); llvm::errs() << Message << "\n"; } } Index: clang/lib/Basic/DiagnosticIDs.cpp =================================================================== --- clang/lib/Basic/DiagnosticIDs.cpp +++ clang/lib/Basic/DiagnosticIDs.cpp @@ -33,7 +33,8 @@ // platforms. See "How To Write Shared Libraries" by Ulrich Drepper. struct StaticDiagInfoDescriptionStringTable { #define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ char ENUM##_desc[sizeof(DESC)]; // clang-format off #include "clang/Basic/DiagnosticCommonKinds.inc" @@ -54,7 +55,8 @@ const StaticDiagInfoDescriptionStringTable StaticDiagInfoDescriptions = { #define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ DESC, // clang-format off #include "clang/Basic/DiagnosticCommonKinds.inc" @@ -69,7 +71,7 @@ #include "clang/Basic/DiagnosticSemaKinds.inc" #include "clang/Basic/DiagnosticAnalysisKinds.inc" #include "clang/Basic/DiagnosticRefactoringKinds.inc" - // clang-format on +// clang-format on #undef DIAG }; @@ -79,7 +81,8 @@ // StaticDiagInfoRec would have extra padding on 64-bit platforms. const uint32_t StaticDiagInfoDescriptionOffsets[] = { #define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ offsetof(StaticDiagInfoDescriptionStringTable, ENUM##_desc), // clang-format off #include "clang/Basic/DiagnosticCommonKinds.inc" @@ -93,18 +96,158 @@ #include "clang/Basic/DiagnosticCrossTUKinds.inc" #include "clang/Basic/DiagnosticSemaKinds.inc" #include "clang/Basic/DiagnosticAnalysisKinds.inc" +#include "clang/Basic/DiagnosticRefactoringKinds.inc" +// clang-format on +#undef DIAG +}; + +// Reason.Legacy +struct StaticDiagInfoUnstructuredReasonStringTable { +#define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ + char ENUM##_legacy_reason[sizeof(LEGACY_REASON)]; + // clang-format off +#include "clang/Basic/DiagnosticCommonKinds.inc" +#include "clang/Basic/DiagnosticDriverKinds.inc" +#include "clang/Basic/DiagnosticFrontendKinds.inc" +#include "clang/Basic/DiagnosticSerializationKinds.inc" +#include "clang/Basic/DiagnosticLexKinds.inc" +#include "clang/Basic/DiagnosticParseKinds.inc" +#include "clang/Basic/DiagnosticASTKinds.inc" +#include "clang/Basic/DiagnosticCommentKinds.inc" +#include "clang/Basic/DiagnosticCrossTUKinds.inc" +#include "clang/Basic/DiagnosticSemaKinds.inc" +#include "clang/Basic/DiagnosticAnalysisKinds.inc" #include "clang/Basic/DiagnosticRefactoringKinds.inc" // clang-format on #undef DIAG }; +const StaticDiagInfoUnstructuredReasonStringTable + StaticDiagInfoUnstructuredReasons = { +#define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ + LEGACY_REASON, +// clang-format off +#include "clang/Basic/DiagnosticCommonKinds.inc" +#include "clang/Basic/DiagnosticDriverKinds.inc" +#include "clang/Basic/DiagnosticFrontendKinds.inc" +#include "clang/Basic/DiagnosticSerializationKinds.inc" +#include "clang/Basic/DiagnosticLexKinds.inc" +#include "clang/Basic/DiagnosticParseKinds.inc" +#include "clang/Basic/DiagnosticASTKinds.inc" +#include "clang/Basic/DiagnosticCommentKinds.inc" +#include "clang/Basic/DiagnosticCrossTUKinds.inc" +#include "clang/Basic/DiagnosticSemaKinds.inc" +#include "clang/Basic/DiagnosticAnalysisKinds.inc" +#include "clang/Basic/DiagnosticRefactoringKinds.inc" +// clang-format on +#undef DIAG +}; + +// Stored separately from StaticDiagInfoRec to pack better. Otherwise, +// StaticDiagInfoRec would have extra padding on 64-bit platforms. +const uint32_t StaticDiagInfoUnstructuredReasonOffsets[] = { +#define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ + offsetof(StaticDiagInfoUnstructuredReasonStringTable, ENUM##_legacy_reason), +// clang-format off +#include "clang/Basic/DiagnosticCommonKinds.inc" +#include "clang/Basic/DiagnosticDriverKinds.inc" +#include "clang/Basic/DiagnosticFrontendKinds.inc" +#include "clang/Basic/DiagnosticSerializationKinds.inc" +#include "clang/Basic/DiagnosticLexKinds.inc" +#include "clang/Basic/DiagnosticParseKinds.inc" +#include "clang/Basic/DiagnosticASTKinds.inc" +#include "clang/Basic/DiagnosticCommentKinds.inc" +#include "clang/Basic/DiagnosticCrossTUKinds.inc" +#include "clang/Basic/DiagnosticSemaKinds.inc" +#include "clang/Basic/DiagnosticAnalysisKinds.inc" +#include "clang/Basic/DiagnosticRefactoringKinds.inc" +// clang-format on +#undef DIAG +}; + +// Reason.SARIF +struct StaticDiagInfoStructuredReasonStringTable { +#define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ + char ENUM##_sarif_reason[sizeof(SARIF_REASON)]; + // clang-format off +#include "clang/Basic/DiagnosticCommonKinds.inc" +#include "clang/Basic/DiagnosticDriverKinds.inc" +#include "clang/Basic/DiagnosticFrontendKinds.inc" +#include "clang/Basic/DiagnosticSerializationKinds.inc" +#include "clang/Basic/DiagnosticLexKinds.inc" +#include "clang/Basic/DiagnosticParseKinds.inc" +#include "clang/Basic/DiagnosticASTKinds.inc" +#include "clang/Basic/DiagnosticCommentKinds.inc" +#include "clang/Basic/DiagnosticCrossTUKinds.inc" +#include "clang/Basic/DiagnosticSemaKinds.inc" +#include "clang/Basic/DiagnosticAnalysisKinds.inc" +#include "clang/Basic/DiagnosticRefactoringKinds.inc" + // clang-format on +#undef DIAG +}; + +const StaticDiagInfoStructuredReasonStringTable + StaticDiagInfoStructuredReasons = { +#define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ + SARIF_REASON, +// clang-format off +#include "clang/Basic/DiagnosticCommonKinds.inc" +#include "clang/Basic/DiagnosticDriverKinds.inc" +#include "clang/Basic/DiagnosticFrontendKinds.inc" +#include "clang/Basic/DiagnosticSerializationKinds.inc" +#include "clang/Basic/DiagnosticLexKinds.inc" +#include "clang/Basic/DiagnosticParseKinds.inc" +#include "clang/Basic/DiagnosticASTKinds.inc" +#include "clang/Basic/DiagnosticCommentKinds.inc" +#include "clang/Basic/DiagnosticCrossTUKinds.inc" +#include "clang/Basic/DiagnosticSemaKinds.inc" +#include "clang/Basic/DiagnosticAnalysisKinds.inc" +#include "clang/Basic/DiagnosticRefactoringKinds.inc" +// clang-format on +#undef DIAG +}; + +// Stored separately from StaticDiagInfoRec to pack better. Otherwise, +// StaticDiagInfoRec would have extra padding on 64-bit platforms. +const uint32_t StaticDiagInfoStructuredReasonOffsets[] = { +#define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ + offsetof(StaticDiagInfoStructuredReasonStringTable, ENUM##_sarif_reason), +// clang-format off +#include "clang/Basic/DiagnosticCommonKinds.inc" +#include "clang/Basic/DiagnosticDriverKinds.inc" +#include "clang/Basic/DiagnosticFrontendKinds.inc" +#include "clang/Basic/DiagnosticSerializationKinds.inc" +#include "clang/Basic/DiagnosticLexKinds.inc" +#include "clang/Basic/DiagnosticParseKinds.inc" +#include "clang/Basic/DiagnosticASTKinds.inc" +#include "clang/Basic/DiagnosticCommentKinds.inc" +#include "clang/Basic/DiagnosticCrossTUKinds.inc" +#include "clang/Basic/DiagnosticSemaKinds.inc" +#include "clang/Basic/DiagnosticAnalysisKinds.inc" +#include "clang/Basic/DiagnosticRefactoringKinds.inc" +// clang-format on +#undef DIAG +}; + // Diagnostic classes. enum { - CLASS_NOTE = 0x01, - CLASS_REMARK = 0x02, - CLASS_WARNING = 0x03, - CLASS_EXTENSION = 0x04, - CLASS_ERROR = 0x05 + CLASS_NOTE = 0x01, + CLASS_REMARK = 0x02, + CLASS_WARNING = 0x03, + CLASS_EXTENSION = 0x04, + CLASS_ERROR = 0x05 }; struct StaticDiagInfoRec { @@ -121,18 +264,35 @@ uint16_t Deferrable : 1; uint16_t DescriptionLen; + uint16_t UnstructuredReasonLen; + uint16_t StructuredReasonLen; - unsigned getOptionGroupIndex() const { - return OptionGroupIndex; - } + unsigned getOptionGroupIndex() const { return OptionGroupIndex; } StringRef getDescription() const { size_t MyIndex = this - &StaticDiagInfo[0]; uint32_t StringOffset = StaticDiagInfoDescriptionOffsets[MyIndex]; - const char* Table = reinterpret_cast<const char*>(&StaticDiagInfoDescriptions); + const char *Table = + reinterpret_cast<const char *>(&StaticDiagInfoDescriptions); return StringRef(&Table[StringOffset], DescriptionLen); } + StringRef getUnstructuredReason() const { + size_t MyIndex = this - &StaticDiagInfo[0]; + uint32_t StringOffset = StaticDiagInfoUnstructuredReasonOffsets[MyIndex]; + const char *Table = + reinterpret_cast<const char *>(&StaticDiagInfoUnstructuredReasons); + return StringRef(&Table[StringOffset], UnstructuredReasonLen); + } + + StringRef getStructuredReason() const { + size_t MyIndex = this - &StaticDiagInfo[0]; + uint32_t StringOffset = StaticDiagInfoStructuredReasonOffsets[MyIndex]; + const char *Table = + reinterpret_cast<const char *>(&StaticDiagInfoStructuredReasons); + return StringRef(&Table[StringOffset], StructuredReasonLen); + } + diag::Flavor getFlavor() const { return Class == CLASS_REMARK ? diag::Flavor::Remark : diag::Flavor::WarningOrError; @@ -171,7 +331,7 @@ const StaticDiagInfoRec StaticDiagInfo[] = { // clang-format off #define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, LEGACY_REASON, SARIF_REASON) \ { \ diag::ENUM, \ DEFAULT_SEVERITY, \ @@ -183,7 +343,9 @@ SHOWINSYSMACRO, \ GROUP, \ DEFERRABLE, \ - STR_SIZE(DESC, uint16_t)}, + STR_SIZE(DESC, uint16_t), \ + STR_SIZE(LEGACY_REASON, uint16_t), \ + STR_SIZE(SARIF_REASON, uint16_t)}, #include "clang/Basic/DiagnosticCommonKinds.inc" #include "clang/Basic/DiagnosticDriverKinds.inc" #include "clang/Basic/DiagnosticFrontendKinds.inc" @@ -222,22 +384,22 @@ // memory at all. unsigned Offset = 0; unsigned ID = DiagID - DIAG_START_COMMON - 1; -#define CATEGORY(NAME, PREV) \ - if (DiagID > DIAG_START_##NAME) { \ - Offset += NUM_BUILTIN_##PREV##_DIAGNOSTICS - DIAG_START_##PREV - 1; \ - ID -= DIAG_START_##NAME - DIAG_START_##PREV; \ +#define CATEGORY(NAME, PREV) \ + if (DiagID > DIAG_START_##NAME) { \ + Offset += NUM_BUILTIN_##PREV##_DIAGNOSTICS - DIAG_START_##PREV - 1; \ + ID -= DIAG_START_##NAME - DIAG_START_##PREV; \ } -CATEGORY(DRIVER, COMMON) -CATEGORY(FRONTEND, DRIVER) -CATEGORY(SERIALIZATION, FRONTEND) -CATEGORY(LEX, SERIALIZATION) -CATEGORY(PARSE, LEX) -CATEGORY(AST, PARSE) -CATEGORY(COMMENT, AST) -CATEGORY(CROSSTU, COMMENT) -CATEGORY(SEMA, CROSSTU) -CATEGORY(ANALYSIS, SEMA) -CATEGORY(REFACTORING, ANALYSIS) + CATEGORY(DRIVER, COMMON) + CATEGORY(FRONTEND, DRIVER) + CATEGORY(SERIALIZATION, FRONTEND) + CATEGORY(LEX, SERIALIZATION) + CATEGORY(PARSE, LEX) + CATEGORY(AST, PARSE) + CATEGORY(COMMENT, AST) + CATEGORY(CROSSTU, COMMENT) + CATEGORY(SEMA, CROSSTU) + CATEGORY(ANALYSIS, SEMA) + CATEGORY(REFACTORING, ANALYSIS) #undef CATEGORY // Avoid out of bounds reads. @@ -281,16 +443,14 @@ } namespace { - // The diagnostic category names. - struct StaticDiagCategoryRec { - const char *NameStr; - uint8_t NameLen; +// The diagnostic category names. +struct StaticDiagCategoryRec { + const char *NameStr; + uint8_t NameLen; - StringRef getName() const { - return StringRef(NameStr, NameLen); - } - }; -} + StringRef getName() const { return StringRef(NameStr, NameLen); } +}; +} // namespace // Unfortunately, the split between DiagnosticIDs and Diagnostic is not // particularly clean, but for now we just implement this method here so we can @@ -309,11 +469,10 @@ static const StaticDiagCategoryRec CategoryNameTable[] = { #define GET_CATEGORY_TABLE -#define CATEGORY(X, ENUM) { X, STR_SIZE(X, uint8_t) }, +#define CATEGORY(X, ENUM) {X, STR_SIZE(X, uint8_t)}, #include "clang/Basic/DiagnosticGroups.inc" #undef GET_CATEGORY_TABLE - { nullptr, 0 } -}; + {nullptr, 0}}; /// getNumberOfCategories - Return the number of categories unsigned DiagnosticIDs::getNumberOfCategories() { @@ -325,12 +484,10 @@ /// invalid. StringRef DiagnosticIDs::getCategoryNameFromID(unsigned CategoryID) { if (CategoryID >= getNumberOfCategories()) - return StringRef(); + return StringRef(); return CategoryNameTable[CategoryID].getName(); } - - DiagnosticIDs::SFINAEResponse DiagnosticIDs::getDiagnosticSFINAEResponse(unsigned DiagID) { if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID)) @@ -357,47 +514,82 @@ //===----------------------------------------------------------------------===// namespace clang { - namespace diag { - class CustomDiagInfo { - typedef std::pair<DiagnosticIDs::Level, std::string> DiagDesc; - std::vector<DiagDesc> DiagInfo; - std::map<DiagDesc, unsigned> DiagIDs; - public: - - /// getDescription - Return the description of the specified custom - /// diagnostic. - StringRef getDescription(unsigned DiagID) const { - assert(DiagID - DIAG_UPPER_LIMIT < DiagInfo.size() && - "Invalid diagnostic ID"); - return DiagInfo[DiagID-DIAG_UPPER_LIMIT].second; - } - - /// getLevel - Return the level of the specified custom diagnostic. - DiagnosticIDs::Level getLevel(unsigned DiagID) const { - assert(DiagID - DIAG_UPPER_LIMIT < DiagInfo.size() && - "Invalid diagnostic ID"); - return DiagInfo[DiagID-DIAG_UPPER_LIMIT].first; - } - - unsigned getOrCreateDiagID(DiagnosticIDs::Level L, StringRef Message, - DiagnosticIDs &Diags) { - DiagDesc D(L, std::string(Message)); - // Check to see if it already exists. - std::map<DiagDesc, unsigned>::iterator I = DiagIDs.lower_bound(D); - if (I != DiagIDs.end() && I->first == D) - return I->second; - - // If not, assign a new ID. - unsigned ID = DiagInfo.size()+DIAG_UPPER_LIMIT; - DiagIDs.insert(std::make_pair(D, ID)); - DiagInfo.push_back(D); - return ID; - } - }; - - } // end diag namespace -} // end clang namespace +namespace diag { +class CustomDiagInfo { + struct DiagDesc { + DiagnosticIDs::Level Level; + std::string Message; + DiagnosticReason Reason; + + // FIXME(spaceship): default when C++20 becomes the default + bool operator==(const DiagDesc &Other) const { + return std::tie(Level, Message, Reason) == + std::tie(Other.Level, Other.Message, Other.Reason); + } + bool operator!=(const DiagDesc &Other) const { return !(*this == Other); } + + bool operator<(const DiagDesc &Other) const { + return std::tie(Level, Message, Reason) < + std::tie(Other.Level, Other.Message, Other.Reason); + } + + bool operator>(const DiagDesc &Other) const { return Other < *this; } + + bool operator<=(const DiagDesc &Other) const { return !(Other < *this); } + + bool operator>=(const DiagDesc &Other) const { return !(*this < Other); } + }; + std::vector<DiagDesc> DiagInfo; + std::map<DiagDesc, unsigned> DiagIDs; + + bool IsValidDiagnosticID(unsigned DiagID) const { + return DiagID - DIAG_UPPER_LIMIT < DiagInfo.size(); + } + +public: + /// getDescription - Return the description of the specified custom + /// diagnostic. + StringRef getDescription(unsigned DiagID) const { + assert(IsValidDiagnosticID(DiagID) && "Invalid diagnostic ID"); + return DiagInfo[DiagID - DIAG_UPPER_LIMIT].Message; + } + + StringRef getLegacyReason(unsigned DiagID) const { + assert(IsValidDiagnosticID(DiagID) && "Invalid diagnostic ID"); + return DiagInfo[DiagID - DIAG_UPPER_LIMIT].Reason.Legacy; + } + + StringRef getSARIFReason(unsigned DiagID) const { + assert(IsValidDiagnosticID(DiagID) && "Invalid diagnostic ID"); + return DiagInfo[DiagID - DIAG_UPPER_LIMIT].Reason.SARIF; + } + + /// getLevel - Return the level of the specified custom diagnostic. + DiagnosticIDs::Level getLevel(unsigned DiagID) const { + assert(IsValidDiagnosticID(DiagID) && "Invalid diagnostic ID"); + return DiagInfo[DiagID - DIAG_UPPER_LIMIT].Level; + } + + unsigned getOrCreateDiagID(DiagnosticIDs::Level L, StringRef Message, + const DiagnosticReason &Reason, + DiagnosticIDs &Diags) { + DiagDesc D{L, std::string(Message), Reason}; + // Check to see if it already exists. + std::map<DiagDesc, unsigned>::iterator I = DiagIDs.lower_bound(D); + if (I != DiagIDs.end() && I->first == D) + return I->second; + + // If not, assign a new ID. + unsigned ID = DiagInfo.size() + DIAG_UPPER_LIMIT; + DiagIDs.insert(std::make_pair(D, ID)); + DiagInfo.push_back(D); + return ID; + } +}; + +} // namespace diag +} // namespace clang //===----------------------------------------------------------------------===// // Common Diagnostic implementation @@ -413,13 +605,13 @@ /// /// \param FormatString A fixed diagnostic format string that will be hashed and /// mapped to a unique DiagID. -unsigned DiagnosticIDs::getCustomDiagID(Level L, StringRef FormatString) { +unsigned DiagnosticIDs::getCustomDiagID(Level L, StringRef FormatString, + const DiagnosticReason &Reason) { if (!CustomDiagInfo) CustomDiagInfo.reset(new diag::CustomDiagInfo()); - return CustomDiagInfo->getOrCreateDiagID(L, FormatString, *this); + return CustomDiagInfo->getOrCreateDiagID(L, FormatString, Reason, *this); } - /// isBuiltinWarningOrExtension - Return true if the unmapped diagnostic /// level of the specified diagnostic ID is a Warning or Extension. /// This only works on builtin diagnostics, not custom ones, and is not legal to @@ -433,7 +625,7 @@ /// Note. bool DiagnosticIDs::isBuiltinNote(unsigned DiagID) { return DiagID < diag::DIAG_UPPER_LIMIT && - getBuiltinDiagClass(DiagID) == CLASS_NOTE; + getBuiltinDiagClass(DiagID) == CLASS_NOTE; } /// isBuiltinExtensionDiag - Determine whether the given built-in diagnostic @@ -442,7 +634,7 @@ /// which case -pedantic enables it) or treated as a warning/error by default. /// bool DiagnosticIDs::isBuiltinExtensionDiag(unsigned DiagID, - bool &EnabledByDefault) { + bool &EnabledByDefault) { if (DiagID >= diag::DIAG_UPPER_LIMIT || getBuiltinDiagClass(DiagID) != CLASS_EXTENSION) return false; @@ -468,6 +660,20 @@ return CustomDiagInfo->getDescription(DiagID); } +StringRef DiagnosticIDs::getLegacyReason(unsigned DiagID) const { + if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID)) + return Info->getUnstructuredReason(); + assert(CustomDiagInfo && "Invalid CustomDiagInfo"); + return CustomDiagInfo->getLegacyReason(DiagID); +} + +StringRef DiagnosticIDs::getSARIFReason(unsigned DiagID) const { + if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID)) + return Info->getStructuredReason(); + assert(CustomDiagInfo && "Invalid CustomDiagInfo"); + return CustomDiagInfo->getSARIFReason(DiagID); +} + static DiagnosticIDs::Level toLevel(diag::Severity SV) { switch (SV) { case diag::Severity::Ignored: @@ -497,7 +703,8 @@ } unsigned DiagClass = getBuiltinDiagClass(DiagID); - if (DiagClass == CLASS_NOTE) return DiagnosticIDs::Note; + if (DiagClass == CLASS_NOTE) + return DiagnosticIDs::Note; return toLevel(getDiagnosticSeverity(DiagID, Loc, Diag)); } @@ -603,19 +810,19 @@ #undef GET_DIAG_ARRAYS namespace { - struct WarningOption { - uint16_t NameOffset; - uint16_t Members; - uint16_t SubGroups; - StringRef Documentation; - - // String is stored with a pascal-style length byte. - StringRef getName() const { - return StringRef(DiagGroupNames + NameOffset + 1, - DiagGroupNames[NameOffset]); - } - }; -} +struct WarningOption { + uint16_t NameOffset; + uint16_t Members; + uint16_t SubGroups; + StringRef Documentation; + + // String is stored with a pascal-style length byte. + StringRef getName() const { + return StringRef(DiagGroupNames + NameOffset + 1, + DiagGroupNames[NameOffset]); + } +}; +} // namespace // Second the table of options, sorted by name for fast binary lookup. static const WarningOption OptionTable[] = { @@ -694,15 +901,15 @@ // Add the members of the subgroups. const int16_t *SubGroups = DiagSubGroups + Group->SubGroups; for (; *SubGroups != (int16_t)-1; ++SubGroups) - NotFound &= getDiagnosticsInGroup(Flavor, &OptionTable[(short)*SubGroups], - Diags); + NotFound &= + getDiagnosticsInGroup(Flavor, &OptionTable[(short)*SubGroups], Diags); return NotFound; } -bool -DiagnosticIDs::getDiagnosticsInGroup(diag::Flavor Flavor, StringRef Group, - SmallVectorImpl<diag::kind> &Diags) const { +bool DiagnosticIDs::getDiagnosticsInGroup( + diag::Flavor Flavor, StringRef Group, + SmallVectorImpl<diag::kind> &Diags) const { if (llvm::Optional<diag::Group> G = getGroupForWarningOption(Group)) return ::getDiagnosticsInGroup( Flavor, &OptionTable[static_cast<unsigned>(*G)], Diags); @@ -756,8 +963,8 @@ // Figure out the diagnostic level of this message. unsigned DiagID = Info.getID(); - DiagnosticIDs::Level DiagLevel - = getDiagnosticLevel(DiagID, Info.getLocation(), Diag); + DiagnosticIDs::Level DiagLevel = + getDiagnosticLevel(DiagID, Info.getLocation(), Diag); // Update counts for DiagnosticErrorTrap even if a fatal error occurred // or diagnostics are suppressed. @@ -832,7 +1039,8 @@ void DiagnosticIDs::EmitDiag(DiagnosticsEngine &Diag, Level DiagLevel) const { Diagnostic Info(&Diag); - assert(DiagLevel != DiagnosticIDs::Ignored && "Cannot emit ignored diagnostics!"); + assert(DiagLevel != DiagnosticIDs::Ignored && + "Cannot emit ignored diagnostics!"); Diag.Client->HandleDiagnostic((DiagnosticsEngine::Level)DiagLevel, Info); if (Diag.Client->IncludeInDiagnosticCounts()) { Index: clang/lib/Basic/Diagnostic.cpp =================================================================== --- clang/lib/Basic/Diagnostic.cpp +++ clang/lib/Basic/Diagnostic.cpp @@ -791,8 +791,7 @@ /// FormatDiagnostic - Format this diagnostic into a string, substituting the /// formal arguments into the %0 slots. The result is appended onto the Str /// array. -void Diagnostic:: -FormatDiagnostic(SmallVectorImpl<char> &OutStr) const { +void Diagnostic::FormatSummary(SmallVectorImpl<char> &OutStr) const { if (!StoredDiagMessage.empty()) { OutStr.append(StoredDiagMessage.begin(), StoredDiagMessage.end()); return; @@ -804,6 +803,16 @@ FormatDiagnostic(Diag.begin(), Diag.end(), OutStr); } +void Diagnostic::FormatLegacyReason(SmallVectorImpl<char> &OutStr) const { + StringRef Diag = getDiags()->getDiagnosticIDs()->getLegacyReason(getID()); + FormatDiagnostic(Diag.begin(), Diag.end(), OutStr); +} + +void Diagnostic::FormatSARIFReason(SmallVectorImpl<char> &OutStr) const { + StringRef Diag = getDiags()->getDiagnosticIDs()->getSARIFReason(getID()); + FormatDiagnostic(Diag.begin(), Diag.end(), OutStr); +} + /// pushEscapedString - Append Str to the diagnostic buffer, /// escaping non-printable characters and ill-formed code unit sequences. static void pushEscapedString(StringRef Str, SmallVectorImpl<char> &OutStr) { @@ -1154,8 +1163,8 @@ } StoredDiagnostic::StoredDiagnostic(DiagnosticsEngine::Level Level, unsigned ID, - StringRef Message) - : ID(ID), Level(Level), Message(Message) {} + StringRef Message, DiagnosticReason Reason) + : ID(ID), Level(Level), Message(Message), Reason(std::move(Reason)) {} StoredDiagnostic::StoredDiagnostic(DiagnosticsEngine::Level Level, const Diagnostic &Info) @@ -1165,20 +1174,20 @@ if (Info.getLocation().isValid()) Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager()); SmallString<64> Message; - Info.FormatDiagnostic(Message); + Info.FormatSummary(Message); this->Message.assign(Message.begin(), Message.end()); this->Ranges.assign(Info.getRanges().begin(), Info.getRanges().end()); this->FixIts.assign(Info.getFixItHints().begin(), Info.getFixItHints().end()); } StoredDiagnostic::StoredDiagnostic(DiagnosticsEngine::Level Level, unsigned ID, - StringRef Message, FullSourceLoc Loc, + StringRef Message, DiagnosticReason Reason, + FullSourceLoc Loc, ArrayRef<CharSourceRange> Ranges, ArrayRef<FixItHint> FixIts) - : ID(ID), Level(Level), Loc(Loc), Message(Message), - Ranges(Ranges.begin(), Ranges.end()), FixIts(FixIts.begin(), FixIts.end()) -{ -} + : ID(ID), Level(Level), Loc(Loc), Message(Message), Reason(Reason), + Ranges(Ranges.begin(), Ranges.end()), + FixIts(FixIts.begin(), FixIts.end()) {} llvm::raw_ostream &clang::operator<<(llvm::raw_ostream &OS, const StoredDiagnostic &SD) { Index: clang/include/clang/Frontend/DiagnosticRenderer.h =================================================================== --- clang/include/clang/Frontend/DiagnosticRenderer.h +++ clang/include/clang/Frontend/DiagnosticRenderer.h @@ -127,6 +127,7 @@ /// \param Loc The location for this caret. /// \param Level The level of the diagnostic to be emitted. /// \param Message The diagnostic message to emit. + /// \param Reason Supplementary information for the message. /// \param Ranges The underlined ranges for this code snippet. /// \param FixItHints The FixIt hints active for this diagnostic. void emitDiagnostic(FullSourceLoc Loc, DiagnosticsEngine::Level Level, Index: clang/include/clang/Frontend/ASTUnit.h =================================================================== --- clang/include/clang/Frontend/ASTUnit.h +++ clang/include/clang/Frontend/ASTUnit.h @@ -99,6 +99,7 @@ unsigned ID; DiagnosticsEngine::Level Level; std::string Message; + DiagnosticReason Reason; std::string Filename; unsigned LocOffset; std::vector<std::pair<unsigned, unsigned>> Ranges; Index: clang/include/clang/Basic/PartialDiagnostic.h =================================================================== --- clang/include/clang/Basic/PartialDiagnostic.h +++ clang/include/clang/Basic/PartialDiagnostic.h @@ -170,7 +170,9 @@ // messing with the state of the diagnostics engine. DiagnosticBuilder DB(Diags.Report(getDiagID())); Emit(DB); - Diagnostic(&Diags).FormatDiagnostic(Buf); + Diagnostic Info(&Diags); + Info.FormatSummary(Buf); + Info.FormatLegacyReason(Buf); DB.Clear(); Diags.Clear(); } Index: clang/include/clang/Basic/DiagnosticSerialization.h =================================================================== --- clang/include/clang/Basic/DiagnosticSerialization.h +++ clang/include/clang/Basic/DiagnosticSerialization.h @@ -15,7 +15,8 @@ namespace diag { enum { #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ ENUM, #define SERIALIZATIONSTART #include "clang/Basic/DiagnosticSerializationKinds.inc" Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4515,8 +4515,12 @@ "of conflicting types for parameter %0 (%1 of type $ vs. %3 of type $)|" "%1 and %3 of conflicting types for parameter %0}2,4">; def note_ovl_candidate_explicit_arg_mismatch_named : Note< - "candidate template ignored: invalid explicitly-specified argument " - "for template parameter %0">; + "candidate template ignored: ", + DiagReason< + /*Legacy:*/"invalid explicitly-specified argument for template parameter %0", + /*SARIF:*/"we passed a %select{type|value|class template}1 as our %ordinal2 " + "explicit template parameter, but this candidate expects a " + "%select{type|value|class template}3 for template parameter %0">>; def note_ovl_candidate_unsatisfied_constraints : Note< "candidate template ignored: constraints not satisfied%0">; def note_ovl_candidate_explicit_arg_mismatch_unnamed : Note< Index: clang/include/clang/Basic/DiagnosticSema.h =================================================================== --- clang/include/clang/Basic/DiagnosticSema.h +++ clang/include/clang/Basic/DiagnosticSema.h @@ -15,7 +15,8 @@ namespace diag { enum { #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ ENUM, #define SEMASTART #include "clang/Basic/DiagnosticSemaKinds.inc" Index: clang/include/clang/Basic/DiagnosticRefactoring.h =================================================================== --- clang/include/clang/Basic/DiagnosticRefactoring.h +++ clang/include/clang/Basic/DiagnosticRefactoring.h @@ -15,7 +15,8 @@ namespace diag { enum { #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ ENUM, #define REFACTORINGSTART #include "clang/Basic/DiagnosticRefactoringKinds.inc" Index: clang/include/clang/Basic/DiagnosticParse.h =================================================================== --- clang/include/clang/Basic/DiagnosticParse.h +++ clang/include/clang/Basic/DiagnosticParse.h @@ -15,7 +15,8 @@ namespace diag { enum { #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ ENUM, #define PARSESTART #include "clang/Basic/DiagnosticParseKinds.inc" Index: clang/include/clang/Basic/DiagnosticLex.h =================================================================== --- clang/include/clang/Basic/DiagnosticLex.h +++ clang/include/clang/Basic/DiagnosticLex.h @@ -15,7 +15,8 @@ namespace diag { enum { #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ ENUM, #define LEXSTART #include "clang/Basic/DiagnosticLexKinds.inc" Index: clang/include/clang/Basic/DiagnosticIDs.h =================================================================== --- clang/include/clang/Basic/DiagnosticIDs.h +++ clang/include/clang/Basic/DiagnosticIDs.h @@ -66,9 +66,10 @@ // Get typedefs for common diagnostics. enum { -#define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, CATEGORY, \ - NOWERROR, SHOWINSYSHEADER, SHOWINSYSMACRO, DEFFERABLE) \ - ENUM, +#define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, CATEGORY, \ + NOWERROR, SHOWINSYSHEADER, SHOWINSYSMACRO, DEFFERABLE, \ + LEGACY_REASON, SARIF_REASON) \ + ENUM, #define COMMONSTART #include "clang/Basic/DiagnosticCommonKinds.inc" NUM_BUILTIN_COMMON_DIAGNOSTICS @@ -160,6 +161,34 @@ } }; +struct DiagnosticReason { + std::string Legacy; + std::string SARIF; + + // FIXME(spaceship): default when C++20 becomes the default + bool operator==(const DiagnosticReason &Other) const { + return Legacy == Other.Legacy && SARIF == Other.SARIF; + } + + bool operator!=(const DiagnosticReason &Other) const { + return !(*this == Other); + } + + bool operator<(const DiagnosticReason &Other) const { + return Legacy < Other.Legacy && SARIF < Other.SARIF; + } + + bool operator>(const DiagnosticReason &Other) const { return Other < *this; } + + bool operator<=(const DiagnosticReason &Other) const { + return !(Other < *this); + } + + bool operator>=(const DiagnosticReason &Other) const { + return !(*this < Other); + } +}; + /// Used for handling and querying diagnostic IDs. /// /// Can be used and shared by multiple Diagnostics for multiple translation units. @@ -187,7 +216,8 @@ // FIXME: Replace this function with a create-only facilty like // createCustomDiagIDFromFormatString() to enforce safe usage. At the time of // writing, nearly all callers of this function were invalid. - unsigned getCustomDiagID(Level L, StringRef FormatString); + unsigned getCustomDiagID(Level L, StringRef FormatString, + const DiagnosticReason &Reason = {}); //===--------------------------------------------------------------------===// // Diagnostic classification and reporting interfaces. @@ -196,6 +226,12 @@ /// Given a diagnostic ID, return a description of the issue. StringRef getDescription(unsigned DiagID) const; + /// Given a diagnostic ID, return the issue's legacy reason. + StringRef getLegacyReason(unsigned DiagID) const; + + /// Given a diagnostic ID, return the issue's SARIF reason. + StringRef getSARIFReason(unsigned DiagID) const; + /// Return true if the unmapped diagnostic levelof the specified /// diagnostic ID is a Warning or Extension. /// Index: clang/include/clang/Basic/DiagnosticFrontend.h =================================================================== --- clang/include/clang/Basic/DiagnosticFrontend.h +++ clang/include/clang/Basic/DiagnosticFrontend.h @@ -15,7 +15,8 @@ namespace diag { enum { #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ ENUM, #define FRONTENDSTART #include "clang/Basic/DiagnosticFrontendKinds.inc" Index: clang/include/clang/Basic/DiagnosticDriver.h =================================================================== --- clang/include/clang/Basic/DiagnosticDriver.h +++ clang/include/clang/Basic/DiagnosticDriver.h @@ -15,7 +15,8 @@ namespace diag { enum { #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ ENUM, #define DRIVERSTART #include "clang/Basic/DiagnosticDriverKinds.inc" Index: clang/include/clang/Basic/DiagnosticCrossTU.h =================================================================== --- clang/include/clang/Basic/DiagnosticCrossTU.h +++ clang/include/clang/Basic/DiagnosticCrossTU.h @@ -15,7 +15,8 @@ namespace diag { enum { #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ ENUM, #define CROSSTUSTART #include "clang/Basic/DiagnosticCrossTUKinds.inc" Index: clang/include/clang/Basic/DiagnosticComment.h =================================================================== --- clang/include/clang/Basic/DiagnosticComment.h +++ clang/include/clang/Basic/DiagnosticComment.h @@ -15,7 +15,8 @@ namespace diag { enum { #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ ENUM, #define COMMENTSTART #include "clang/Basic/DiagnosticCommentKinds.inc" Index: clang/include/clang/Basic/DiagnosticAnalysis.h =================================================================== --- clang/include/clang/Basic/DiagnosticAnalysis.h +++ clang/include/clang/Basic/DiagnosticAnalysis.h @@ -15,7 +15,8 @@ namespace diag { enum { #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ ENUM, #define ANALYSISSTART #include "clang/Basic/DiagnosticAnalysisKinds.inc" Index: clang/include/clang/Basic/DiagnosticAST.h =================================================================== --- clang/include/clang/Basic/DiagnosticAST.h +++ clang/include/clang/Basic/DiagnosticAST.h @@ -15,7 +15,8 @@ namespace diag { enum { #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ ENUM, #define ASTSTART #include "clang/Basic/DiagnosticASTKinds.inc" Index: clang/include/clang/Basic/Diagnostic.td =================================================================== --- clang/include/clang/Basic/Diagnostic.td +++ clang/include/clang/Basic/Diagnostic.td @@ -73,9 +73,25 @@ // This defines all of the named diagnostic groups. include "DiagnosticGroups.td" +class DiagReason<string legacy, string structured = legacy> { + // A legacy reason is a reason that is part of Clang's legacy diagnostic model, + // but needed to be split from the summary in order to facilitate the existence + // of the structured diagnostic (which will have very different text). One may + // observe that older versions of Clang's diagnostics are the same as + // `Diagnostic.Summary + Diagnostic.Reason.Legacy`. This is intentional, so as + // to preserve the legacy diagnostic model for the time being. + string Legacy = legacy; + + // The SARIF reason will appear when Clang is asked to emit SARIF diagnostics, + // in place of Legacy. Unlike Legacy, which is a fragment of the original + // diagnostic from past-Clang, SARIF reasons may have completely different text + // to explain the problem, often from the perspective of a user. + string SARIF = structured; +} +def NO_REASON_YET : DiagReason<"">; // All diagnostics emitted by the compiler are an indirect subclass of this. -class Diagnostic<string summary, DiagClass DC, Severity defaultmapping> { +class Diagnostic<string summary, DiagClass DC, Severity defaultmapping, DiagReason reason> { /// Component is specified by the file with a big let directive. string Component = ?; string Summary = summary; @@ -89,6 +105,7 @@ Severity DefaultSeverity = defaultmapping; DiagGroup Group; string CategoryName = ""; + DiagReason Reason = reason; } class SFINAEFailure { @@ -126,24 +143,24 @@ } // FIXME: ExtWarn and Extension should also be SFINAEFailure by default. -class Error<string str> : Diagnostic<str, CLASS_ERROR, SEV_Error>, SFINAEFailure { +class Error<string str, DiagReason reason = NO_REASON_YET> : Diagnostic<str, CLASS_ERROR, SEV_Error, reason>, SFINAEFailure { bit ShowInSystemHeader = 1; } // Warnings default to on (but can be default-off'd with DefaultIgnore). // This is used for warnings about questionable code; warnings about // accepted language extensions should use Extension or ExtWarn below instead. -class Warning<string str> : Diagnostic<str, CLASS_WARNING, SEV_Warning>; +class Warning<string str, DiagReason reason = NO_REASON_YET> : Diagnostic<str, CLASS_WARNING, SEV_Warning, reason>; // Remarks can be turned on with -R flags and provide commentary, e.g. on // optimizer decisions. -class Remark<string str> : Diagnostic<str, CLASS_REMARK, SEV_Ignored>; +class Remark<string str, DiagReason reason = NO_REASON_YET> : Diagnostic<str, CLASS_REMARK, SEV_Ignored, reason>; // Extensions are warnings about accepted language extensions. // Extension warnings are default-off but enabled by -pedantic. -class Extension<string str> : Diagnostic<str, CLASS_EXTENSION, SEV_Ignored>; +class Extension<string str, DiagReason reason = NO_REASON_YET> : Diagnostic<str, CLASS_EXTENSION, SEV_Ignored, reason>; // ExtWarns are warnings about accepted language extensions. // ExtWarn warnings are default-on. -class ExtWarn<string str> : Diagnostic<str, CLASS_EXTENSION, SEV_Warning>; +class ExtWarn<string str, DiagReason reason = NO_REASON_YET> : Diagnostic<str, CLASS_EXTENSION, SEV_Warning, reason>; // Notes can provide supplementary information on errors, warnings, and remarks. -class Note<string str> : Diagnostic<str, CLASS_NOTE, SEV_Fatal/*ignored*/>; +class Note<string str, DiagReason reason = NO_REASON_YET> : Diagnostic<str, CLASS_NOTE, SEV_Fatal/*ignored*/, reason>; class DefaultIgnore { Severity DefaultSeverity = SEV_Ignored; } Index: clang/include/clang/Basic/Diagnostic.h =================================================================== --- clang/include/clang/Basic/Diagnostic.h +++ clang/include/clang/Basic/Diagnostic.h @@ -1666,11 +1666,23 @@ return DiagObj->DiagStorage.FixItHints; } - /// Format this diagnostic into a string, substituting the + /// Format this summary into a string, substituting the /// formal arguments into the %0 slots. /// /// The result is appended onto the \p OutStr array. - void FormatDiagnostic(SmallVectorImpl<char> &OutStr) const; + void FormatSummary(SmallVectorImpl<char> &OutStr) const; + + /// Format the legacy reason into a string, substituting the formal arguments + /// into the %0 slots. + /// + /// The result is appended onto the \p OutStr array. + void FormatLegacyReason(SmallVectorImpl<char> &OutStr) const; + + /// Format the SARIF-based reason into a string, substituting the formal + /// arguments into the %0 slots. + /// + /// The result is appended onto the \p OutStr array. + void FormatSARIFReason(SmallVectorImpl<char> &OutStr) const; /// Format the given format-string into the output buffer using the /// arguments stored in this diagnostic. @@ -1687,6 +1699,8 @@ DiagnosticsEngine::Level Level; FullSourceLoc Loc; std::string Message; + DiagnosticReason Reason; + std::vector<CharSourceRange> Ranges; std::vector<FixItHint> FixIts; @@ -1694,10 +1708,10 @@ StoredDiagnostic() = default; StoredDiagnostic(DiagnosticsEngine::Level Level, const Diagnostic &Info); StoredDiagnostic(DiagnosticsEngine::Level Level, unsigned ID, - StringRef Message); + StringRef Message, DiagnosticReason Reason); StoredDiagnostic(DiagnosticsEngine::Level Level, unsigned ID, - StringRef Message, FullSourceLoc Loc, - ArrayRef<CharSourceRange> Ranges, + StringRef Message, DiagnosticReason Reason, + FullSourceLoc Loc, ArrayRef<CharSourceRange> Ranges, ArrayRef<FixItHint> Fixits); /// Evaluates true when this object stores a diagnostic. @@ -1707,6 +1721,7 @@ DiagnosticsEngine::Level getLevel() const { return Level; } const FullSourceLoc &getLocation() const { return Loc; } StringRef getMessage() const { return Message; } + const DiagnosticReason &getReason() const { return Reason; } void setLocation(FullSourceLoc Loc) { this->Loc = Loc; } Index: clang-tools-extra/clangd/Diagnostics.cpp =================================================================== --- clang-tools-extra/clangd/Diagnostics.cpp +++ clang-tools-extra/clangd/Diagnostics.cpp @@ -42,7 +42,8 @@ const char *getDiagnosticCode(unsigned ID) { switch (ID) { #define DIAG(ENUM, CLASS, DEFAULT_MAPPING, DESC, GROPU, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, \ + LEGACY_REASON, SARIF_REASON) \ case clang::diag::ENUM: \ return #ENUM; #include "clang/Basic/DiagnosticASTKinds.inc" @@ -653,7 +654,8 @@ const clang::Diagnostic &Info, clangd::DiagBase &D) { llvm::SmallString<64> Message; - Info.FormatDiagnostic(Message); + Info.FormatSummary(Message); + Info.FormatLegacyReason(Message); D.Message = std::string(Message.str()); D.Severity = DiagLevel; @@ -781,7 +783,8 @@ } } if (Message.empty()) // either !SyntheticMessage, or we failed to make one. - Info.FormatDiagnostic(Message); + Info.FormatSummary(Message); + Info.FormatLegacyReason(Message); LastDiag->Fixes.push_back( Fix{std::string(Message.str()), std::move(Edits)}); return true; Index: clang-tools-extra/clangd/Compiler.cpp =================================================================== --- clang-tools-extra/clangd/Compiler.cpp +++ clang-tools-extra/clangd/Compiler.cpp @@ -21,7 +21,8 @@ const clang::Diagnostic &Info) { // FIXME: format lazily, in case vlog is off. llvm::SmallString<64> Message; - Info.FormatDiagnostic(Message); + Info.FormatSummary(Message); + Info.FormatLegacyReason(Message); llvm::SmallString<64> Location; if (Info.hasSourceManager() && Info.getLocation().isValid()) { Index: clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp =================================================================== --- clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp +++ clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp @@ -418,7 +418,8 @@ Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(), Errors.back()); SmallString<100> Message; - Info.FormatDiagnostic(Message); + Info.FormatSummary(Message); + Info.FormatLegacyReason(Message); FullSourceLoc Loc; if (Info.getLocation().isValid() && Info.hasSourceManager()) Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager());
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits