Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'enum_select' diagnostic selection to clang. #122505

Merged
merged 3 commits into from
Jan 15, 2025

Conversation

erichkeane
Copy link
Collaborator

This causes us to generate an enum to go along with the select diagnostic, which allows for clearer diagnostic error emit lines.

The syntax for this is:

%enum_select{%OptionalEnumeratorName{Text}|{Text2}}0

Where the curley brackets around the select-text are only required if an Enumerator name is provided.

The TableGen here emits this as a normal 'select' to the frontend, which permits us to reuse all of the existing 'select' infrastructure. Documentation is the same as well.

This causes us to generate an enum to go along with the select
diagnostic, which allows for clearer diagnostic error emit lines.

The syntax for this is:

%enum_select<EnumerationName>{%OptionalEnumeratorName{Text}|{Text2}}0

Where the curley brackets around the select-text are only required if an
Enumerator name is provided.

The TableGen here emits this as a normal 'select' to the frontend, which
permits us to reuse all of the existing 'select' infrastructure.
Documentation is the same as well.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jan 10, 2025
@llvmbot
Copy link
Member

llvmbot commented Jan 10, 2025

@llvm/pr-subscribers-clang

Author: Erich Keane (erichkeane)

Changes

This causes us to generate an enum to go along with the select diagnostic, which allows for clearer diagnostic error emit lines.

The syntax for this is:

%enum_select<EnumerationName>{%OptionalEnumeratorName{Text}|{Text2}}0

Where the curley brackets around the select-text are only required if an Enumerator name is provided.

The TableGen here emits this as a normal 'select' to the frontend, which permits us to reuse all of the existing 'select' infrastructure. Documentation is the same as well.


Patch is 30.67 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/122505.diff

20 Files Affected:

  • (modified) clang/include/clang/Basic/CMakeLists.txt (+5)
  • (modified) clang/include/clang/Basic/DiagnosticAST.h (+13)
  • (modified) clang/include/clang/Basic/DiagnosticAnalysis.h (+12)
  • (modified) clang/include/clang/Basic/DiagnosticComment.h (+13)
  • (modified) clang/include/clang/Basic/DiagnosticCrossTU.h (+13)
  • (modified) clang/include/clang/Basic/DiagnosticDriver.h (+13)
  • (modified) clang/include/clang/Basic/DiagnosticFrontend.h (+13)
  • (modified) clang/include/clang/Basic/DiagnosticInstallAPI.h (+13)
  • (modified) clang/include/clang/Basic/DiagnosticLex.h (+12)
  • (modified) clang/include/clang/Basic/DiagnosticParse.h (+13)
  • (modified) clang/include/clang/Basic/DiagnosticRefactoring.h (+13)
  • (modified) clang/include/clang/Basic/DiagnosticSema.h (+13)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+5-3)
  • (modified) clang/include/clang/Basic/DiagnosticSerialization.h (+13)
  • (modified) clang/lib/Sema/SemaDeclCXX.cpp (+13-12)
  • (added) clang/test/TableGen/select-enum-errors.td (+16)
  • (added) clang/test/TableGen/select-enum.td (+26)
  • (modified) clang/utils/TableGen/ClangDiagnosticsEmitter.cpp (+190-2)
  • (modified) clang/utils/TableGen/TableGen.cpp (+6)
  • (modified) clang/utils/TableGen/TableGenBackends.h (+2)
diff --git a/clang/include/clang/Basic/CMakeLists.txt b/clang/include/clang/Basic/CMakeLists.txt
index 897a610b7f9089..56c27bacdb20b8 100644
--- a/clang/include/clang/Basic/CMakeLists.txt
+++ b/clang/include/clang/Basic/CMakeLists.txt
@@ -3,6 +3,11 @@ macro(clang_diag_gen component)
     -gen-clang-diags-defs -clang-component=${component}
     SOURCE Diagnostic.td
     TARGET ClangDiagnostic${component})
+
+  clang_tablegen(Diagnostic${component}Enums.inc
+    -gen-clang-diags-enums -clang-component=${component}
+    SOURCE Diagnostic.td
+    TARGET ClangDiagnostic${component}Enums)
 endmacro(clang_diag_gen)
 
 clang_diag_gen(Analysis)
diff --git a/clang/include/clang/Basic/DiagnosticAST.h b/clang/include/clang/Basic/DiagnosticAST.h
index 24ef2689eac01e..4f82114b7406be 100644
--- a/clang/include/clang/Basic/DiagnosticAST.h
+++ b/clang/include/clang/Basic/DiagnosticAST.h
@@ -22,6 +22,19 @@ enum {
 #undef DIAG
   NUM_BUILTIN_AST_DIAGNOSTICS
 };
+
+#define DIAG_ENUM(ENUM_NAME)                                                   \
+  namespace ENUM_NAME {                                                        \
+  enum {
+#define DIAG_ENUM_ITEM(IDX, NAME) NAME = IDX,
+#define DIAG_ENUM_END()                                                        \
+  }                                                                            \
+  ;                                                                            \
+  }
+#include "clang/Basic/DiagnosticASTEnums.inc"
+#undef DIAG_ENUM_END
+#undef DIAG_ENUM_ITEM
+#undef DIAG_ENUM
 } // end namespace diag
 } // end namespace clang
 
diff --git a/clang/include/clang/Basic/DiagnosticAnalysis.h b/clang/include/clang/Basic/DiagnosticAnalysis.h
index 676b58f7d6ef2c..1a49461bcd1738 100644
--- a/clang/include/clang/Basic/DiagnosticAnalysis.h
+++ b/clang/include/clang/Basic/DiagnosticAnalysis.h
@@ -22,6 +22,18 @@ enum {
 #undef DIAG
   NUM_BUILTIN_ANALYSIS_DIAGNOSTICS
 };
+#define DIAG_ENUM(ENUM_NAME)                                                   \
+  namespace ENUM_NAME {                                                        \
+  enum {
+#define DIAG_ENUM_ITEM(IDX, NAME) NAME = IDX,
+#define DIAG_ENUM_END()                                                        \
+  }                                                                            \
+  ;                                                                            \
+  }
+#include "clang/Basic/DiagnosticAnalysisEnums.inc"
+#undef DIAG_ENUM_END
+#undef DIAG_ENUM_ITEM
+#undef DIAG_ENUM
 } // end namespace diag
 } // end namespace clang
 
diff --git a/clang/include/clang/Basic/DiagnosticComment.h b/clang/include/clang/Basic/DiagnosticComment.h
index 17c0053e9a33da..53143ef132e4b4 100644
--- a/clang/include/clang/Basic/DiagnosticComment.h
+++ b/clang/include/clang/Basic/DiagnosticComment.h
@@ -22,6 +22,19 @@ enum {
 #undef DIAG
   NUM_BUILTIN_COMMENT_DIAGNOSTICS
 };
+
+#define DIAG_ENUM(ENUM_NAME)                                                   \
+  namespace ENUM_NAME {                                                        \
+  enum {
+#define DIAG_ENUM_ITEM(IDX, NAME) NAME = IDX,
+#define DIAG_ENUM_END()                                                        \
+  }                                                                            \
+  ;                                                                            \
+  }
+#include "clang/Basic/DiagnosticCommentEnums.inc"
+#undef DIAG_ENUM_END
+#undef DIAG_ENUM_ITEM
+#undef DIAG_ENUM
 } // end namespace diag
 } // end namespace clang
 
diff --git a/clang/include/clang/Basic/DiagnosticCrossTU.h b/clang/include/clang/Basic/DiagnosticCrossTU.h
index 4341bf327b69c0..428da95011027e 100644
--- a/clang/include/clang/Basic/DiagnosticCrossTU.h
+++ b/clang/include/clang/Basic/DiagnosticCrossTU.h
@@ -22,6 +22,19 @@ enum {
 #undef DIAG
   NUM_BUILTIN_CROSSTU_DIAGNOSTICS
 };
+
+#define DIAG_ENUM(ENUM_NAME)                                                   \
+  namespace ENUM_NAME {                                                        \
+  enum {
+#define DIAG_ENUM_ITEM(IDX, NAME) NAME = IDX,
+#define DIAG_ENUM_END()                                                        \
+  }                                                                            \
+  ;                                                                            \
+  }
+#include "clang/Basic/DiagnosticCrossTUEnums.inc"
+#undef DIAG_ENUM_END
+#undef DIAG_ENUM_ITEM
+#undef DIAG_ENUM
 } // end namespace diag
 } // end namespace clang
 
diff --git a/clang/include/clang/Basic/DiagnosticDriver.h b/clang/include/clang/Basic/DiagnosticDriver.h
index 6931bd46542e86..c472afa3f6e967 100644
--- a/clang/include/clang/Basic/DiagnosticDriver.h
+++ b/clang/include/clang/Basic/DiagnosticDriver.h
@@ -22,6 +22,19 @@ enum {
 #undef DIAG
   NUM_BUILTIN_DRIVER_DIAGNOSTICS
 };
+
+#define DIAG_ENUM(ENUM_NAME)                                                   \
+  namespace ENUM_NAME {                                                        \
+  enum {
+#define DIAG_ENUM_ITEM(IDX, NAME) NAME = IDX,
+#define DIAG_ENUM_END()                                                        \
+  }                                                                            \
+  ;                                                                            \
+  }
+#include "clang/Basic/DiagnosticDriverEnums.inc"
+#undef DIAG_ENUM_END
+#undef DIAG_ENUM_ITEM
+#undef DIAG_ENUM
 } // end namespace diag
 } // end namespace clang
 
diff --git a/clang/include/clang/Basic/DiagnosticFrontend.h b/clang/include/clang/Basic/DiagnosticFrontend.h
index ab4e855f2de029..766cac3d655b3e 100644
--- a/clang/include/clang/Basic/DiagnosticFrontend.h
+++ b/clang/include/clang/Basic/DiagnosticFrontend.h
@@ -22,6 +22,19 @@ enum {
 #undef DIAG
   NUM_BUILTIN_FRONTEND_DIAGNOSTICS
 };
+
+#define DIAG_ENUM(ENUM_NAME)                                                   \
+  namespace ENUM_NAME {                                                        \
+  enum {
+#define DIAG_ENUM_ITEM(IDX, NAME) NAME = IDX,
+#define DIAG_ENUM_END()                                                        \
+  }                                                                            \
+  ;                                                                            \
+  }
+#include "clang/Basic/DiagnosticFrontendEnums.inc"
+#undef DIAG_ENUM_END
+#undef DIAG_ENUM_ITEM
+#undef DIAG_ENUM
 } // end namespace diag
 } // end namespace clang
 
diff --git a/clang/include/clang/Basic/DiagnosticInstallAPI.h b/clang/include/clang/Basic/DiagnosticInstallAPI.h
index a76f6e087a2b0a..cbdb00362624b7 100644
--- a/clang/include/clang/Basic/DiagnosticInstallAPI.h
+++ b/clang/include/clang/Basic/DiagnosticInstallAPI.h
@@ -21,6 +21,19 @@ enum {
 #undef DIAG
   NUM_BUILTIN_INSTALLAPI_DIAGNOSTICS
 };
+
+#define DIAG_ENUM(ENUM_NAME)                                                   \
+  namespace ENUM_NAME {                                                        \
+  enum {
+#define DIAG_ENUM_ITEM(IDX, NAME) NAME = IDX,
+#define DIAG_ENUM_END()                                                        \
+  }                                                                            \
+  ;                                                                            \
+  }
+#include "clang/Basic/DiagnosticInstallAPIEnums.inc"
+#undef DIAG_ENUM_END
+#undef DIAG_ENUM_ITEM
+#undef DIAG_ENUM
 } // namespace diag
 } // namespace clang
 #endif // LLVM_CLANG_BASIC_DIAGNOSTICINSTALLAPI_H
diff --git a/clang/include/clang/Basic/DiagnosticLex.h b/clang/include/clang/Basic/DiagnosticLex.h
index 5f237085ae03a1..d14bf97e8642ed 100644
--- a/clang/include/clang/Basic/DiagnosticLex.h
+++ b/clang/include/clang/Basic/DiagnosticLex.h
@@ -22,6 +22,18 @@ enum {
 #undef DIAG
   NUM_BUILTIN_LEX_DIAGNOSTICS
 };
+#define DIAG_ENUM(ENUM_NAME)                                                   \
+  namespace ENUM_NAME {                                                        \
+  enum {
+#define DIAG_ENUM_ITEM(IDX, NAME) NAME = IDX,
+#define DIAG_ENUM_END()                                                        \
+  }                                                                            \
+  ;                                                                            \
+  }
+#include "clang/Basic/DiagnosticLexEnums.inc"
+#undef DIAG_ENUM_END
+#undef DIAG_ENUM_ITEM
+#undef DIAG_ENUM
 } // end namespace diag
 } // end namespace clang
 
diff --git a/clang/include/clang/Basic/DiagnosticParse.h b/clang/include/clang/Basic/DiagnosticParse.h
index 81a8185d25fb70..275e1a4c39b3fa 100644
--- a/clang/include/clang/Basic/DiagnosticParse.h
+++ b/clang/include/clang/Basic/DiagnosticParse.h
@@ -22,6 +22,19 @@ enum {
 #undef DIAG
   NUM_BUILTIN_PARSE_DIAGNOSTICS
 };
+
+#define DIAG_ENUM(ENUM_NAME)                                                   \
+  namespace ENUM_NAME {                                                        \
+  enum {
+#define DIAG_ENUM_ITEM(IDX, NAME) NAME = IDX,
+#define DIAG_ENUM_END()                                                        \
+  }                                                                            \
+  ;                                                                            \
+  }
+#include "clang/Basic/DiagnosticParseEnums.inc"
+#undef DIAG_ENUM_END
+#undef DIAG_ENUM_ITEM
+#undef DIAG_ENUM
 } // end namespace diag
 } // end namespace clang
 
diff --git a/clang/include/clang/Basic/DiagnosticRefactoring.h b/clang/include/clang/Basic/DiagnosticRefactoring.h
index 9b628dbeb7c268..59d4bc912733aa 100644
--- a/clang/include/clang/Basic/DiagnosticRefactoring.h
+++ b/clang/include/clang/Basic/DiagnosticRefactoring.h
@@ -22,6 +22,19 @@ enum {
 #undef DIAG
   NUM_BUILTIN_REFACTORING_DIAGNOSTICS
 };
+
+#define DIAG_ENUM(ENUM_NAME)                                                   \
+  namespace ENUM_NAME {                                                        \
+  enum {
+#define DIAG_ENUM_ITEM(IDX, NAME) NAME = IDX,
+#define DIAG_ENUM_END()                                                        \
+  }                                                                            \
+  ;                                                                            \
+  }
+#include "clang/Basic/DiagnosticRefactoringEnums.inc"
+#undef DIAG_ENUM_END
+#undef DIAG_ENUM_ITEM
+#undef DIAG_ENUM
 } // end namespace diag
 } // end namespace clang
 
diff --git a/clang/include/clang/Basic/DiagnosticSema.h b/clang/include/clang/Basic/DiagnosticSema.h
index 45014fe21271d8..84986c7bccf71d 100644
--- a/clang/include/clang/Basic/DiagnosticSema.h
+++ b/clang/include/clang/Basic/DiagnosticSema.h
@@ -22,6 +22,19 @@ enum {
 #undef DIAG
   NUM_BUILTIN_SEMA_DIAGNOSTICS
 };
+
+#define DIAG_ENUM(ENUM_NAME)                                                   \
+  namespace ENUM_NAME {                                                        \
+  enum {
+#define DIAG_ENUM_ITEM(IDX, NAME) NAME = IDX,
+#define DIAG_ENUM_END()                                                        \
+  }                                                                            \
+  ;                                                                            \
+  }
+#include "clang/Basic/DiagnosticSemaEnums.inc"
+#undef DIAG_ENUM_END
+#undef DIAG_ENUM_ITEM
+#undef DIAG_ENUM
 } // end namespace diag
 } // end namespace clang
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d4e897868f1a9a..bbac4d3fe71a61 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -617,9 +617,11 @@ def err_ambiguous_inherited_constructor : Error<
   "constructor of %0 inherited from multiple base class subobjects">;
 def note_ambiguous_inherited_constructor_using : Note<
   "inherited from base class %0 here">;
-def note_using_decl_class_member_workaround : Note<
-  "use %select{an alias declaration|a typedef declaration|a reference|"
-  "a const variable|a constexpr variable}0 instead">;
+def note_using_decl_class_member_workaround
+    : Note<"use %enum_select<MemClassWorkaround>{%AliasDecl{an alias "
+           "declaration}|%TypedefDecl{a typedef declaration}|%ReferenceDecl{a "
+           "reference}|%ConstVar{a const variable}|%ConstexprVar{a constexpr "
+           "variable}}0 instead">;
 def err_using_decl_can_not_refer_to_namespace : Error<
   "using declaration cannot refer to a namespace">;
 def note_namespace_using_decl : Note<
diff --git a/clang/include/clang/Basic/DiagnosticSerialization.h b/clang/include/clang/Basic/DiagnosticSerialization.h
index 0c622a5657737b..6fb836dca1b040 100644
--- a/clang/include/clang/Basic/DiagnosticSerialization.h
+++ b/clang/include/clang/Basic/DiagnosticSerialization.h
@@ -22,6 +22,19 @@ enum {
 #undef DIAG
   NUM_BUILTIN_SERIALIZATION_DIAGNOSTICS
 };
+
+#define DIAG_ENUM(ENUM_NAME)                                                   \
+  namespace ENUM_NAME {                                                        \
+  enum {
+#define DIAG_ENUM_ITEM(IDX, NAME) NAME = IDX,
+#define DIAG_ENUM_END()                                                        \
+  }                                                                            \
+  ;                                                                            \
+  }
+#include "clang/Basic/DiagnosticSerializationEnums.inc"
+#undef DIAG_ENUM_END
+#undef DIAG_ENUM_ITEM
+#undef DIAG_ENUM
 } // end namespace diag
 } // end namespace clang
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index c5a72cf812ebc9..eb8a9c85c8ebb9 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -13217,18 +13217,18 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc, bool HasTypename,
         if (getLangOpts().CPlusPlus11) {
           // Convert 'using X::Y;' to 'using Y = X::Y;'.
           Diag(SS.getBeginLoc(), diag::note_using_decl_class_member_workaround)
-            << 0 // alias declaration
-            << FixItHint::CreateInsertion(SS.getBeginLoc(),
-                                          NameInfo.getName().getAsString() +
-                                              " = ");
+              << diag::MemClassWorkaround::AliasDecl
+              << FixItHint::CreateInsertion(SS.getBeginLoc(),
+                                            NameInfo.getName().getAsString() +
+                                                " = ");
         } else {
           // Convert 'using X::Y;' to 'typedef X::Y Y;'.
           SourceLocation InsertLoc = getLocForEndOfToken(NameInfo.getEndLoc());
           Diag(InsertLoc, diag::note_using_decl_class_member_workaround)
-            << 1 // typedef declaration
-            << FixItHint::CreateReplacement(UsingLoc, "typedef")
-            << FixItHint::CreateInsertion(
-                   InsertLoc, " " + NameInfo.getName().getAsString());
+              << diag::MemClassWorkaround::TypedefDecl
+              << FixItHint::CreateReplacement(UsingLoc, "typedef")
+              << FixItHint::CreateInsertion(
+                     InsertLoc, " " + NameInfo.getName().getAsString());
         }
       } else if (R->getAsSingle<VarDecl>()) {
         // Don't provide a fixit outside C++11 mode; we don't want to suggest
@@ -13241,8 +13241,7 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc, bool HasTypename,
         }
 
         Diag(UsingLoc, diag::note_using_decl_class_member_workaround)
-          << 2 // reference declaration
-          << FixIt;
+            << diag::MemClassWorkaround::ReferenceDecl << FixIt;
       } else if (R->getAsSingle<EnumConstantDecl>()) {
         // Don't provide a fixit outside C++11 mode; we don't want to suggest
         // repeating the type of the enumeration here, and we can't do so if
@@ -13256,8 +13255,10 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc, bool HasTypename,
         }
 
         Diag(UsingLoc, diag::note_using_decl_class_member_workaround)
-          << (getLangOpts().CPlusPlus11 ? 4 : 3) // const[expr] variable
-          << FixIt;
+            << (getLangOpts().CPlusPlus11
+                    ? diag::MemClassWorkaround::ConstexprVar
+                    : diag::MemClassWorkaround::ConstVar)
+            << FixIt;
       }
     }
 
diff --git a/clang/test/TableGen/select-enum-errors.td b/clang/test/TableGen/select-enum-errors.td
new file mode 100644
index 00000000000000..a2e8c09361760f
--- /dev/null
+++ b/clang/test/TableGen/select-enum-errors.td
@@ -0,0 +1,16 @@
+// RUN: clang-tblgen --gen-clang-diags-enums -I%S %s 2>&1 | FileCheck %s
+include "DiagnosticBase.inc"
+
+def DupeNames1 : Error<"%enum_select<DupeName>{}0">;
+def DupeNames2 : Error<"%enum_select<DupeName>{}0">;
+// CHECK: error: Duplicate enumeration name 'DupeName'
+// CHECK-NEXT: def DupeNames2
+// CHECK: note: Previous diagnostic is here
+// CHECK-NEXT: def DupeNames1
+
+def DupeValue : Error<"%enum_select<DupeValue>{%Name{V1}|%Name{V2}}0">;
+// CHECK: error: Duplicate enumerator name 'Name'
+
+def EnumValNotExpected : Error<"%enum_select{V1|%Val2{V2}}0">;
+// CHECK: expected '<' after enum_select
+
diff --git a/clang/test/TableGen/select-enum.td b/clang/test/TableGen/select-enum.td
new file mode 100644
index 00000000000000..8a92acec62cfb0
--- /dev/null
+++ b/clang/test/TableGen/select-enum.td
@@ -0,0 +1,26 @@
+// RUN: clang-tblgen --gen-clang-diags-enums -I%S %s 2>&1 | FileCheck %s
+include "DiagnosticBase.inc"
+
+def Diag : Error<"%enum_select<EnumName>{%Val1{V1}|%Val2{V2}|%Val3{V3}}0">;
+// CHECK: DIAG_ENUM(EnumName)
+// CHECK-NEXT: DIAG_ENUM_ITEM(0, Val1)
+// CHECK-NEXT: DIAG_ENUM_ITEM(1, Val2)
+// CHECK-NEXT: DIAG_ENUM_ITEM(2, Val3)
+// CHECK-NEXT: DIAG_ENUM_END()
+
+// These are OK, we permit missing values since they might not be useful.
+def Missing1 : Error<"%enum_select<DupeEnumName1>{V1|%Val2{V2}|%Val3{V3}}0">;
+// CHECK: DIAG_ENUM(DupeEnumName1)
+// CHECK-NEXT: DIAG_ENUM_ITEM(1, Val2)
+// CHECK-NEXT: DIAG_ENUM_ITEM(2, Val3)
+// CHECK-NEXT: DIAG_ENUM_END()
+def Missing2 : Error<"%enum_select<DupeEnumName2>{%Val1{V1}|V2|%Val3{V3}}0">;
+// CHECK: DIAG_ENUM(DupeEnumName2)
+// CHECK-NEXT: DIAG_ENUM_ITEM(0, Val1)
+// CHECK-NEXT: DIAG_ENUM_ITEM(2, Val3)
+// CHECK-NEXT: DIAG_ENUM_END()
+def Missing3 : Error<"%enum_select<DupeEnumName3>{%Val1{V1}|%Val2{V2}|V3}0">;
+// CHECK: DIAG_ENUM(DupeEnumName3)
+// CHECK-NEXT: DIAG_ENUM_ITEM(0, Val1)
+// CHECK-NEXT: DIAG_ENUM_ITEM(1, Val2)
+// CHECK-NEXT: DIAG_ENUM_END()
diff --git a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp
index 72b3468dac486f..824f0682f5531d 100644
--- a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp
+++ b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp
@@ -399,6 +399,7 @@ enum PieceKind {
   TextPieceClass,
   PlaceholderPieceClass,
   SelectPieceClass,
+  EnumSelectPieceClass,
   PluralPieceClass,
   DiffPieceClass,
   SubstitutionPieceClass,
@@ -408,6 +409,7 @@ enum ModifierType {
   MT_Unknown,
   MT_Placeholder,
   MT_Select,
+  MT_EnumSelect,
   MT_Sub,
   MT_Plural,
   MT_Diff,
@@ -421,6 +423,7 @@ enum ModifierType {
 
 static StringRef getModifierName(ModifierType MT) {
   switch (MT) {
+  case MT_EnumSelect:
   case MT_Select:
     return "select";
   case MT_Sub:
@@ -512,10 +515,26 @@ struct SelectPiece : Piece {
 
   static bool classof(const Piece *P) {
     return P->getPieceClass() == SelectPieceClass ||
+           P->getPieceClass() == EnumSelectPieceClass ||
            P->getPieceClass() == PluralPieceClass;
   }
 };
 
+struct EnumSelectPiece : SelectPiece {
+  EnumSelectPiece() : SelectPiece(EnumSelectPieceClass, MT_EnumSelect) {}
+
+  StringRef EnumName;
+  std::vector<StringRef> OptionEnumNames;
+
+  static bool classof(const Piece *P) {
+    return P->getPieceClass() == EnumSelectPieceClass;
+  }
+};
+
+struct EnumValuePiece : Piece {
+  ModifierType Kind;
+};
+
 struct PluralPiece : SelectPiece {
   PluralPiece() : SelectPiece(PluralPieceClass, MT_Plural) {}
 
@@ -579,6 +598,9 @@ struct DiagnosticTextBuilder {
   std::vector<std::string> buildForDocumentation(StringRef Role,
                                                  const Record *R);
   std::string buildForDefinition(const Record *R);
+  llvm::SmallVector<std::pair<
+      std::string, llvm::SmallVector<std::pair<unsigned, std::string>>>>
+  buildForEnum(const Record *R);
 
   Piece *getSubstitution(SubstitutionPiece *S) const...
[truncated]

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for working on this! We should probably also update https://clang.llvm.org/docs/InternalsManual.html#formatting-a-diagnostic-argument so that folks know how to use this properly and we advertise it more broadly.

-Update internals-manual

-Update document generator comment, clarify we intend to document as if
it is a 'select'

-Add test coverage for other modifiers using '<'

-Add additional suggested test coverage
Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with a minor nit on the documentation, but I think this is a great new feature for the diagnostics engine. Thank you for it!

btw, if you happen to have a list of diagnostics that could benefit from switching to use it, that sure seems like Good First Issue territory.

Change anonymous to unscoped.  The name of it is irrelevant to what we're talking about anyway, since it is unscoped.

Co-authored-by: Aaron Ballman <[email protected]>
@erichkeane
Copy link
Collaborator Author

LGTM with a minor nit on the documentation, but I think this is a great new feature for the diagnostics engine. Thank you for it!

btw, if you happen to have a list of diagnostics that could benefit from switching to use it, that sure seems like Good First Issue territory.

Hmm... I don't. I DID start walking down the list of diagnostics with 'large' selects at one point, but stopped at the first one I could find. Perhaps I'll create a bug as a 'starter' bug with some guidance how to FIND these, and and hope someone comes along and does such an audit

@erichkeane erichkeane merged commit bf17016 into llvm:main Jan 15, 2025
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants