cjdb created this revision.
cjdb added reviewers: dblaikie, erichkeane.
Herald added subscribers: kadircet, arphaman.
Herald added a project: All.
cjdb requested review of this revision.
Herald added projects: clang, libc++abi, clang-tools-extra.
Herald added subscribers: cfe-commits, libcxx-commits.
Herald added a reviewer: libc++abi.

Structured diagnostics enable us to have diagnostics that contain a headline
description and a detailed description. DiagReason represents the latter,
although some subengines might combine the two into a single message.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D140125

Files:
  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/Frontend/ASTUnit.h
  clang/lib/Basic/Diagnostic.cpp
  clang/lib/Basic/DiagnosticIDs.cpp
  clang/lib/Frontend/ASTUnit.cpp
  clang/test/TableGen/DiagnosticBase.inc
  clang/test/TableGen/deferred-diag.td
  clang/tools/diagtool/DiagnosticNames.cpp
  clang/utils/TableGen/ClangDiagnosticsEmitter.cpp
  libcxxabi/test/test_demangle.pass.cpp

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,12 @@
 
     // Category number.
     OS << ", " << CategoryIDs.getID(getDiagnosticCategory(&R, DGParentMap));
+
+    // Reasons
+    OS << ", /*Reason=*/\"";
+    OS.write_escaped(DiagTextBuilder.buildForDefinition(
+        R.getValueAsDef("Reason"), "Value"))
+        << '"';
     OS << ")\n";
   }
 }
Index: clang/tools/diagtool/DiagnosticNames.cpp
===================================================================
--- clang/tools/diagtool/DiagnosticNames.cpp
+++ clang/tools/diagtool/DiagnosticNames.cpp
@@ -28,7 +28,7 @@
 // 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, REASON)         \
   {#ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t)},
 #include "clang/Basic/DiagnosticCommonKinds.inc"
 #include "clang/Basic/DiagnosticCrossTUKinds.inc"
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=*/"")
 
 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=*/"")
 
 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=*/"")
 
 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=*/"")
 
 // 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=*/"")
 
 }
Index: clang/test/TableGen/DiagnosticBase.inc
===================================================================
--- clang/test/TableGen/DiagnosticBase.inc
+++ clang/test/TableGen/DiagnosticBase.inc
@@ -64,10 +64,17 @@
 class InGroup<DiagGroup G> { DiagGroup Group = G; }
 //class IsGroup<string Name> { DiagGroup Group = DiagGroup<Name>; }
 
+// Structured diagnostics enable us to have diagnostics that contain a headline
+// description and a detailed description. DiagReason represents the latter,
+// although some subengines might combine the two into a single message.
+class DiagReason<string value> {
+  string Value = value;
+}
+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 +88,7 @@
   Severity       DefaultSeverity = defaultmapping;
   DiagGroup      Group;
   string         CategoryName = "";
+  DiagReason     Reason = reason;
 }
 
 class SFINAEFailure {
@@ -118,24 +126,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/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/Basic/DiagnosticIDs.cpp
===================================================================
--- clang/lib/Basic/DiagnosticIDs.cpp
+++ clang/lib/Basic/DiagnosticIDs.cpp
@@ -33,7 +33,7 @@
 // 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, REASON)    \
   char ENUM##_desc[sizeof(DESC)];
   // clang-format off
 #include "clang/Basic/DiagnosticCommonKinds.inc"
@@ -54,7 +54,8 @@
 
 const StaticDiagInfoDescriptionStringTable StaticDiagInfoDescriptions = {
 #define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR,     \
-             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY)            \
+             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY,            \
+             REASON)                                      \
   DESC,
 // clang-format off
 #include "clang/Basic/DiagnosticCommonKinds.inc"
@@ -69,7 +70,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 +80,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,            \
+             REASON)                                      \
   offsetof(StaticDiagInfoDescriptionStringTable, ENUM##_desc),
 // clang-format off
 #include "clang/Basic/DiagnosticCommonKinds.inc"
@@ -93,11 +95,81 @@
 #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 StaticDiagInfoReasonStringTable {
+#define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR,     \
+             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY,            \
+             REASON)                                      \
+  char ENUM##_reason[sizeof(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 StaticDiagInfoReasonStringTable
+    StaticDiagInfoReasons = {
+#define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR,     \
+             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY,            \
+             REASON)                                      \
+  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 StaticDiagInfoReasonOffsets[] = {
+#define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR,     \
+             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY,            \
+             REASON)                                      \
+  offsetof(StaticDiagInfoReasonStringTable, ENUM##_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,
@@ -121,6 +193,7 @@
   uint16_t Deferrable : 1;
 
   uint16_t DescriptionLen;
+  uint16_t ReasonLen;
 
   unsigned getOptionGroupIndex() const {
     return OptionGroupIndex;
@@ -133,6 +206,14 @@
     return StringRef(&Table[StringOffset], DescriptionLen);
   }
 
+  StringRef getReason() const {
+    size_t MyIndex = this - &StaticDiagInfo[0];
+    uint32_t StringOffset = StaticDiagInfoReasonOffsets[MyIndex];
+    const char *Table =
+        reinterpret_cast<const char *>(&StaticDiagInfoReasons);
+    return StringRef(&Table[StringOffset], ReasonLen);
+  }
+
   diag::Flavor getFlavor() const {
     return Class == CLASS_REMARK ? diag::Flavor::Remark
                                  : diag::Flavor::WarningOrError;
@@ -171,7 +252,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, REASON)    \
   {                                                                            \
       diag::ENUM,                                                              \
       DEFAULT_SEVERITY,                                                        \
@@ -183,7 +264,8 @@
       SHOWINSYSMACRO,                                                          \
       GROUP,                                                                   \
 	    DEFERRABLE,                                                              \
-      STR_SIZE(DESC, uint16_t)},
+      STR_SIZE(DESC, uint16_t), \
+      STR_SIZE(REASON, uint16_t)},
 #include "clang/Basic/DiagnosticCommonKinds.inc"
 #include "clang/Basic/DiagnosticDriverKinds.inc"
 #include "clang/Basic/DiagnosticFrontendKinds.inc"
@@ -222,22 +304,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,15 +363,15 @@
 }
 
 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);
+  }
+};
 }
 
 // Unfortunately, the split between DiagnosticIDs and Diagnostic is not
@@ -325,7 +407,7 @@
 /// invalid.
 StringRef DiagnosticIDs::getCategoryNameFromID(unsigned CategoryID) {
   if (CategoryID >= getNumberOfCategories())
-   return StringRef();
+    return StringRef();
   return CategoryNameTable[CategoryID].getName();
 }
 
@@ -359,29 +441,60 @@
 namespace clang {
   namespace diag {
     class CustomDiagInfo {
-      typedef std::pair<DiagnosticIDs::Level, std::string> DiagDesc;
+      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;
-    public:
 
+      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(DiagID - DIAG_UPPER_LIMIT < DiagInfo.size() &&
-               "Invalid diagnostic ID");
-        return DiagInfo[DiagID-DIAG_UPPER_LIMIT].second;
+        assert(IsValidDiagnosticID(DiagID) && "Invalid diagnostic ID");
+        return DiagInfo[DiagID - DIAG_UPPER_LIMIT].Message;
+      }
+
+      StringRef getReason(unsigned DiagID) const {
+        assert(IsValidDiagnosticID(DiagID) && "Invalid diagnostic ID");
+        return DiagInfo[DiagID - DIAG_UPPER_LIMIT].Reason.Value;
       }
 
       /// 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;
+        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));
+        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)
@@ -413,10 +526,11 @@
 ///
 /// \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);
 }
 
 
@@ -433,7 +547,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 +556,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 +582,13 @@
   return CustomDiagInfo->getDescription(DiagID);
 }
 
+StringRef DiagnosticIDs::getReason(unsigned DiagID) const {
+  if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID))
+    return Info->getReason();
+  assert(CustomDiagInfo && "Invalid CustomDiagInfo");
+  return CustomDiagInfo->getReason(DiagID);
+}
+
 static DiagnosticIDs::Level toLevel(diag::Severity SV) {
   switch (SV) {
   case diag::Severity::Ignored:
@@ -603,18 +724,18 @@
 #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]);
+  }
+};
 }
 
 // Second the table of options, sorted by name for fast binary lookup.
@@ -702,7 +823,7 @@
 
 bool
 DiagnosticIDs::getDiagnosticsInGroup(diag::Flavor Flavor, StringRef Group,
-                                     SmallVectorImpl<diag::kind> &Diags) const {
+    SmallVectorImpl<diag::kind> &Diags) const {
   if (llvm::Optional<diag::Group> G = getGroupForWarningOption(Group))
     return ::getDiagnosticsInGroup(
         Flavor, &OptionTable[static_cast<unsigned>(*G)], Diags);
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,20 @@
   FormatDiagnostic(Diag.begin(), Diag.end(), OutStr);
 }
 
+void Diagnostic::FormatReason(SmallVectorImpl<char> &OutStr) const {
+  StringRef Diag = getDiags()->getDiagnosticIDs()->getReason(getID());
+  if (!Diag.empty()) {
+    StringRef Colon = ": ";
+    OutStr.append(Colon.begin(), Colon.end());
+    FormatDiagnostic(Diag.begin(), Diag.end(), OutStr);
+  }
+}
+
+void Diagnostic::FormatDiagnostic(SmallVectorImpl<char> &OutStr) const {
+  FormatSummary(OutStr);
+  FormatReason(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 +1164,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)
@@ -1172,13 +1182,13 @@
 }
 
 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/DiagnosticSerialization.h
===================================================================
--- clang/include/clang/Basic/DiagnosticSerialization.h
+++ clang/include/clang/Basic/DiagnosticSerialization.h
@@ -15,7 +15,7 @@
 namespace diag {
 enum {
 #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR,      \
-             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY)            \
+             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, 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
@@ -4503,37 +4503,44 @@
 def note_ovl_candidate_incomplete_deduction : Note<"candidate template ignored: "
     "couldn't infer template argument %0">;
 def note_ovl_candidate_incomplete_deduction_pack : Note<
-    "candidate template ignored: "
-    "deduced too few arguments for expanded pack %0; no argument for %ordinal1 "
-    "expanded parameter in deduced argument pack %2">;
+    "candidate template ignored",
+    DiagReason<"deduced too few arguments for expanded pack %0; no argument for"
+               " %ordinal1 expanded parameter in deduced argument pack %2">>;
 def note_ovl_candidate_inconsistent_deduction : Note<
-    "candidate template ignored: deduced %select{conflicting types|"
-    "conflicting values|conflicting templates|packs of different lengths}0 "
-    "for parameter %1%diff{ ($ vs. $)|}2,3">;
+    "candidate template ignored",
+    DiagReason<"deduced %select{conflicting types|conflicting values|"
+               "conflicting templates|packs of different lengths}0 "
+    "for parameter %1%diff{ ($ vs. $)|}2,3">>;
 def note_ovl_candidate_inconsistent_deduction_types : Note<
-    "candidate template ignored: deduced values %diff{"
-    "of conflicting types for parameter %0 (%1 of type $ vs. %3 of type $)|"
-    "%1 and %3 of conflicting types for parameter %0}2,4">;
+    "candidate template ignored",
+    DiagReason<"deduced values %diff{"
+      "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<"invalid explicitly-specified argument for template parameter %0">>;
 def note_ovl_candidate_unsatisfied_constraints : Note<
-    "candidate template ignored: constraints not satisfied%0">;
+    "candidate template ignored",
+    DiagReason<"constraints not satisfied%0">>;
 def note_ovl_candidate_explicit_arg_mismatch_unnamed : Note<
-    "candidate template ignored: invalid explicitly-specified argument "
-    "for %ordinal0 template parameter">;
+    "candidate template ignored",
+    DiagReason<"invalid explicitly-specified argument for %ordinal0 template "
+               "parameter">>;
 def note_ovl_candidate_instantiation_depth : Note<
-    "candidate template ignored: substitution exceeded maximum template "
-    "instantiation depth">;
+    "candidate template ignored",
+    DiagReason<"substitution exceeded maximum template instantiation depth">>;
 def note_ovl_candidate_underqualified : Note<
-    "candidate template ignored: cannot deduce a type for %0 that would "
-    "make %2 equal %1">;
+    "candidate template ignored",
+    DiagReason<"cannot deduce a type for %0 that would make %2 equal %1">>;
 def note_ovl_candidate_substitution_failure : Note<
-    "candidate template ignored: substitution failure%0%1">;
+    "candidate template ignored",
+    DiagReason<"substitution failure%0%1">>;
 def note_ovl_candidate_disabled_by_enable_if : Note<
-    "candidate template ignored: disabled by %0%1">;
+    "candidate template ignored",
+    DiagReason<"disabled by %0%1">>;
 def note_ovl_candidate_disabled_by_requirement : Note<
-    "candidate template ignored: requirement '%0' was not satisfied%1">;
+    "candidate template ignored",
+    DiagReason<"requirement '%0' was not satisfied%1">>;
 def note_ovl_candidate_has_pass_object_size_params: Note<
     "candidate address cannot be taken because parameter %0 has "
     "pass_object_size attribute">;
Index: clang/include/clang/Basic/DiagnosticSema.h
===================================================================
--- clang/include/clang/Basic/DiagnosticSema.h
+++ clang/include/clang/Basic/DiagnosticSema.h
@@ -15,7 +15,7 @@
 namespace diag {
 enum {
 #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR,      \
-             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY)            \
+             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, 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,7 @@
 namespace diag {
 enum {
 #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR,      \
-             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY)            \
+             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, 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,7 @@
 namespace diag {
 enum {
 #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR,      \
-             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY)            \
+             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, 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,7 @@
 namespace diag {
 enum {
 #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR,      \
-             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY)            \
+             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, 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
@@ -67,7 +67,7 @@
     // Get typedefs for common diagnostics.
     enum {
 #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, CATEGORY,      \
-             NOWERROR, SHOWINSYSHEADER, SHOWINSYSMACRO, DEFFERABLE)            \
+             NOWERROR, SHOWINSYSHEADER, SHOWINSYSMACRO, DEFFERABLE, REASON)    \
   ENUM,
 #define COMMONSTART
 #include "clang/Basic/DiagnosticCommonKinds.inc"
@@ -160,6 +160,18 @@
   }
 };
 
+struct DiagnosticReason {
+  llvm::StringLiteral Value = "";
+
+  // FIXME(spaceship): default when C++20 becomes the default
+  bool operator==(const DiagnosticReason &Other) const { return Value == Other.Value; }
+  bool operator!=(const DiagnosticReason &Other) const { return !(*this == Other); }
+  bool operator<(const DiagnosticReason &Other) const { return Value < Other.Value; }
+  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 +199,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 +209,9 @@
   /// Given a diagnostic ID, return a description of the issue.
   StringRef getDescription(unsigned DiagID) const;
 
+  /// Given a diagnostic ID, return the issue's reason.
+  StringRef getReason(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,7 @@
 namespace diag {
 enum {
 #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR,      \
-             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY)            \
+             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, 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,7 @@
 namespace diag {
 enum {
 #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR,      \
-             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY)            \
+             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, 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,7 @@
 namespace diag {
 enum {
 #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR,      \
-             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY)            \
+             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, 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,7 @@
 namespace diag {
 enum {
 #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR,      \
-             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY)            \
+             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, 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,7 @@
 namespace diag {
 enum {
 #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR,      \
-             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY)            \
+             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, 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,7 @@
 namespace diag {
 enum {
 #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR,      \
-             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY)            \
+             SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, 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,16 @@
 // This defines all of the named diagnostic groups.
 include "DiagnosticGroups.td"
 
+// Structured diagnostics enable us to have diagnostics that contain a headline
+// description and a detailed description. DiagReason represents the latter,
+// although some subengines might combine the two into a single message.
+class DiagReason<string value> {
+  string Value = value;
+}
+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 +96,7 @@
   Severity       DefaultSeverity = defaultmapping;
   DiagGroup      Group;
   string         CategoryName = "";
+  DiagReason     Reason = reason;
 }
 
 class SFINAEFailure {
@@ -126,24 +134,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,6 +1666,18 @@
     return DiagObj->DiagStorage.FixItHints;
   }
 
+  /// Format the summary into a string, substituting the
+  /// formal arguments into the %0 slots.
+  ///
+  /// The result is appended onto the \p OutStr array.
+  void FormatSummary(SmallVectorImpl<char> &OutStr) const;
+
+  /// Format the reason into a string, substituting the formal arguments into
+  /// the %0 slots.
+  ///
+  /// The result is appended onto the \p OutStr array.
+  void FormatReason(SmallVectorImpl<char> &OutStr) const;
+
   /// Format this diagnostic into a string, substituting the
   /// formal arguments into the %0 slots.
   ///
@@ -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,7 @@
 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, REASON)    \
   case clang::diag::ENUM:                                                      \
     return #ENUM;
 #include "clang/Basic/DiagnosticASTKinds.inc"
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to