cjdb updated this revision to Diff 479492.
cjdb marked 4 inline comments as done.
cjdb added a comment.

responds to feedback


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D138939/new/

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/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,7 @@
                         const clang::Diagnostic &info) override {
     if (m_log) {
       llvm::SmallVector<char, 32> diag_str(10);
-      info.FormatDiagnostic(diag_str);
+      info.FormatLegacyDiagnostic(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,7 @@
       Log *log = GetLog(LLDBLog::Expressions);
       if (log) {
         llvm::SmallVector<char, 32> diag_str;
-        Info.FormatDiagnostic(diag_str);
+        Info.FormatLegacyDiagnostic(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.FormatLegacyDiagnostic(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.FormatLegacyDiagnostic(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 << ", /*LegacyReason=*/\"";
+    OS.write_escaped(DiagTextBuilder.buildForDefinition(
+        R.getValueAsDef("Reason")->getValueAsDef("Legacy"), "Value"))
+        << '"';
+
+    OS << ", /*SARIFReason=*/\"";
+    OS.write_escaped(DiagTextBuilder.buildForDefinition(
+        R.getValueAsDef("Reason")->getValueAsDef("SARIF"), "Value"))
+        << '"';
     OS << ")\n";
   }
 }
Index: clang/unittests/Tooling/RewriterTestContext.h
===================================================================
--- clang/unittests/Tooling/RewriterTestContext.h
+++ clang/unittests/Tooling/RewriterTestContext.h
@@ -35,7 +35,7 @@
                         const Diagnostic &Info) override {
     ++NumDiagnosticsSeen;
     SmallString<100> OutStr;
-    Info.FormatDiagnostic(OutStr);
+    Info.FormatLegacyDiagnostic(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,10 @@
     // Capture errors and notes. There should be one of each.
     if (DiagLevel == DiagnosticsEngine::Error) {
       assert(Error.empty());
-      Info.FormatDiagnostic(Error);
+      Info.FormatLegacyDiagnostic(Error);
     } else {
       assert(Note.empty());
-      Info.FormatDiagnostic(Note);
+      Info.FormatLegacyDiagnostic(Note);
     }
   }
   SmallString<32> Error;
Index: clang/unittests/Driver/ToolChainTest.cpp
===================================================================
--- clang/unittests/Driver/ToolChainTest.cpp
+++ clang/unittests/Driver/ToolChainTest.cpp
@@ -378,10 +378,10 @@
                         const Diagnostic &Info) override {
     if (DiagLevel == DiagnosticsEngine::Level::Error) {
       Errors.emplace_back();
-      Info.FormatDiagnostic(Errors.back());
+      Info.FormatLegacyDiagnostic(Errors.back());
     } else {
       Msgs.emplace_back();
-      Info.FormatDiagnostic(Msgs.back());
+      Info.FormatLegacyDiagnostic(Msgs.back());
     }
   }
   void clear() override {
Index: clang/unittests/Driver/SimpleDiagnosticConsumer.h
===================================================================
--- clang/unittests/Driver/SimpleDiagnosticConsumer.h
+++ clang/unittests/Driver/SimpleDiagnosticConsumer.h
@@ -21,10 +21,10 @@
                         const clang::Diagnostic &Info) override {
     if (DiagLevel == clang::DiagnosticsEngine::Level::Error) {
       Errors.emplace_back();
-      Info.FormatDiagnostic(Errors.back());
+      Info.FormatLegacyDiagnostic(Errors.back());
     } else {
       Msgs.emplace_back();
-      Info.FormatDiagnostic(Msgs.back());
+      Info.FormatLegacyDiagnostic(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,7 @@
       }
 
       SmallString<16> DiagText;
-      Info.FormatDiagnostic(DiagText);
+      Info.FormatLegacyDiagnostic(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,7 @@
                         const Diagnostic &Info) override {
 
     SmallVector<char, 16> vec;
-    Info.FormatDiagnostic(vec);
+    Info.FormatLegacyDiagnostic(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,34 @@
 class InGroup<DiagGroup G> { DiagGroup Group = G; }
 //class IsGroup<string Name> { DiagGroup Group = DiagGroup<Name>; }
 
+class LegacyR<string value> {
+  string Value = value;
+}
+
+class SARIF<string value> {
+  string Value = value;
+}
+
+class DiagReason<LegacyR legacy, SARIF sarif = SARIF<legacy.Value>> {
+  // 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.
+  LegacyR 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.
+  SARIF SS = structured;
+}
+def NO_REASON_YET : DiagReason<LegacyR<"">>;
 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 +105,7 @@
   Severity       DefaultSeverity = defaultmapping;
   DiagGroup      Group;
   string         CategoryName = "";
+  DiagReason     Reason = reason;
 }
 
 class SFINAEFailure {
@@ -118,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/test/Frontend/sarif-reason.cpp
===================================================================
--- /dev/null
+++ clang/test/Frontend/sarif-reason.cpp
@@ -0,0 +1,47 @@
+// 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();
+template<int T>
+void f2();
+template<template<class> class T>
+void f3();
+
+template<class> struct S;
+
+void g() {
+  f1<0>(); // expected-error{{no matching function for call to 'f1'}}
+           // expected-note@6{{candidate template ignored: invalid explicitly-specified argument for template parameter 'T'}}
+  f1<S>(); // expected-error{{no matching function for call to 'f1'}}
+           // expected-note@6{{candidate template ignored: invalid explicitly-specified argument for template parameter 'T'}}
+
+  f2<int>(); // expected-error{{no matching function for call to 'f2'}}
+             // expected-note@8{{candidate template ignored: invalid explicitly-specified argument for template parameter 'T'}}
+  f2<S>();   // expected-error{{no matching function for call to 'f2'}}
+             // expected-note@8{{candidate template ignored: invalid explicitly-specified argument for template parameter 'T'}}
+
+  f3<int>(); // expected-error{{no matching function for call to 'f3'}}
+             // expected-note@10{{candidate template ignored: invalid explicitly-specified argument for template parameter 'T'}}
+  f3<0>();   // expected-error{{no matching function for call to 'f3'}}
+             // expected-note@10{{candidate template ignored: invalid explicitly-specified argument for template parameter 'T'}}
+}
+
+// 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,7 @@
   // 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.FormatLegacyDiagnostic(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,7 @@
   DiagnosticConsumer::HandleDiagnostic(Level, Info);
 
   SmallString<100> Buf;
-  Info.FormatDiagnostic(Buf);
+  Info.FormatLegacyDiagnostic(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,7 @@
          "Received a diagnostic after we've already started teardown.");
   if (IsFinishing) {
     SmallString<256> diagnostic;
-    Info.FormatDiagnostic(diagnostic);
+    Info.FormatLegacyDiagnostic(diagnostic);
     getMetaDiags()->Report(
         diag::warn_fe_serialized_diag_failure_during_finalisation)
         << diagnostic;
@@ -594,7 +594,7 @@
 
   // Compute the diagnostic text.
   State->diagBuf.clear();
-  Info.FormatDiagnostic(State->diagBuf);
+  Info.FormatLegacyDiagnostic(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,7 @@
 
   // Format the message.
   SmallString<100> MessageStr;
-  Info.FormatDiagnostic(MessageStr);
+  Info.FormatLegacyDiagnostic(MessageStr);
   DE.Message = std::string(MessageStr.str());
 
   // Set the location information.
@@ -160,4 +160,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,7 @@
     if (DiagLevel == DiagnosticsEngine::Fatal) {
       Fatal = true;
       llvm::SmallVector<char, 128> Message;
-      Info.FormatDiagnostic(Message);
+      Info.FormatLegacyDiagnostic(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.Value;
+  }
+
+  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
@@ -788,11 +788,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 +800,21 @@
   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);
+}
+
+void Diagnostic::FormatLegacyDiagnostic(SmallVectorImpl<char> &OutStr) const {
+  FormatSummary(OutStr);
+  FormatLegacyReason(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 +1165,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 +1176,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/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,8 @@
     //        messing with the state of the diagnostics engine.
     DiagnosticBuilder DB(Diags.Report(getDiagID()));
     Emit(DB);
-    Diagnostic(&Diags).FormatDiagnostic(Buf);
+    Diagnostic Info(&Diags);
+    Info.FormatLegacyDiagnostic(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,46 @@
   }
 };
 
+struct LegacyR {
+  llvm::StringLiteral Value = "";
+
+  // FIXME(spaceship): default when C++20 becomes the default
+  bool operator==(const LegacyR &Other) const { return Value == Other.Value; }
+  bool operator!=(const LegacyR &Other) const { return !(*this == Other); }
+  bool operator<(const LegacyR &Other) const { return Value < Other.Value; }
+  bool operator>(const LegacyR &Other) const { return Other < *this; }
+  bool operator<=(const LegacyR &Other) const { return !(Other < *this); }
+  bool operator>=(const LegacyR &Other) const { return !(*this < Other); }
+};
+
+struct DiagnosticReason {
+  LegacyR Legacy;
+  llvm::StringLiteral 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 +228,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 +238,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,33 @@
 // This defines all of the named diagnostic groups.
 include "DiagnosticGroups.td"
 
+class Legacy<string value> {
+  string Value = value;
+}
+
+class SARIF<string value> {
+  string Value = value;
+}
+
+class DiagReason<Legacy legacy, SARIF structured = SARIF<legacy.Value>> {
+  // 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.
+  Legacy 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.
+  SARIF SARIF = structured;
+}
+def NO_REASON_YET : DiagReason<Legacy<"">, SARIF<"">>;
 
 // 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 +113,7 @@
   Severity       DefaultSeverity = defaultmapping;
   DiagGroup      Group;
   string         CategoryName = "";
+  DiagReason     Reason = reason;
 }
 
 class SFINAEFailure {
@@ -126,24 +151,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,29 @@
     return DiagObj->DiagStorage.FixItHints;
   }
 
-  /// Format this diagnostic into a string, substituting the
+  /// Format the 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 legacy diagnostic into a string, substituting the
+  /// formal arguments into the %0 slots.
+  ///
+  /// The result is appended onto the \p OutStr array.
+  void FormatLegacyDiagnostic(SmallVectorImpl<char> &OutStr) const;
 
   /// Format the given format-string into the output buffer using the
   /// arguments stored in this diagnostic.
@@ -1687,6 +1705,8 @@
   DiagnosticsEngine::Level Level;
   FullSourceLoc Loc;
   std::string Message;
+  DiagnosticReason Reason;
+
   std::vector<CharSourceRange> Ranges;
   std::vector<FixItHint> FixIts;
 
@@ -1694,10 +1714,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 +1727,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,7 @@
                                 const clang::Diagnostic &Info,
                                 clangd::DiagBase &D) {
   llvm::SmallString<64> Message;
-  Info.FormatDiagnostic(Message);
+  Info.FormatLegacyDiagnostic(Message);
 
   D.Message = std::string(Message.str());
   D.Severity = DiagLevel;
@@ -781,7 +782,8 @@
       }
     }
     if (Message.empty()) // either !SyntheticMessage, or we failed to make one.
-      Info.FormatDiagnostic(Message);
+      Info.FormatSummary(Message);
+    Info.FormatLegacyDiagnostic(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,7 @@
                             const clang::Diagnostic &Info) {
   // FIXME: format lazily, in case vlog is off.
   llvm::SmallString<64> Message;
-  Info.FormatDiagnostic(Message);
+  Info.FormatLegacyDiagnostic(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,7 @@
         Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(),
         Errors.back());
     SmallString<100> Message;
-    Info.FormatDiagnostic(Message);
+    Info.FormatLegacyDiagnostic(Message);
     FullSourceLoc Loc;
     if (Info.getLocation().isValid() && Info.hasSourceManager())
       Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager());
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to