[clang] Add 'enum_select' diagnostic selection to clang. (PR #122505)
Erich Keane via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 10 09:58:19 PST 2025
https://github.com/erichkeane created https://github.com/llvm/llvm-project/pull/122505
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.
>From 4fbfdc801f6f296a8021f8efaea3a0da887f276f Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Thu, 9 Jan 2025 10:47:59 -0800
Subject: [PATCH] Add 'enum_select' diagnostic selection to clang.
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.
---
clang/include/clang/Basic/CMakeLists.txt | 5 +
clang/include/clang/Basic/DiagnosticAST.h | 13 ++
.../include/clang/Basic/DiagnosticAnalysis.h | 12 ++
clang/include/clang/Basic/DiagnosticComment.h | 13 ++
clang/include/clang/Basic/DiagnosticCrossTU.h | 13 ++
clang/include/clang/Basic/DiagnosticDriver.h | 13 ++
.../include/clang/Basic/DiagnosticFrontend.h | 13 ++
.../clang/Basic/DiagnosticInstallAPI.h | 13 ++
clang/include/clang/Basic/DiagnosticLex.h | 12 ++
clang/include/clang/Basic/DiagnosticParse.h | 13 ++
.../clang/Basic/DiagnosticRefactoring.h | 13 ++
clang/include/clang/Basic/DiagnosticSema.h | 13 ++
.../clang/Basic/DiagnosticSemaKinds.td | 8 +-
.../clang/Basic/DiagnosticSerialization.h | 13 ++
clang/lib/Sema/SemaDeclCXX.cpp | 25 +--
clang/test/TableGen/select-enum-errors.td | 16 ++
clang/test/TableGen/select-enum.td | 26 +++
.../TableGen/ClangDiagnosticsEmitter.cpp | 192 +++++++++++++++++-
clang/utils/TableGen/TableGen.cpp | 6 +
clang/utils/TableGen/TableGenBackends.h | 2 +
20 files changed, 417 insertions(+), 17 deletions(-)
create mode 100644 clang/test/TableGen/select-enum-errors.td
create mode 100644 clang/test/TableGen/select-enum.td
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 {
auto It = Substitutions.find(S->Name);
@@ -707,6 +729,7 @@ template <class Derived> struct DiagTextVisitor {
CASE(Text);
CASE(Placeholder);
CASE(Select);
+ CASE(EnumSelect);
CASE(Plural);
CASE(Diff);
CASE(Substitution);
@@ -886,6 +909,11 @@ struct DiagTextDocPrinter : DiagTextVisitor<DiagTextDocPrinter> {
makeRowSeparator(RST[I]);
}
+ void VisitEnumSelect(EnumSelectPiece *P) {
+ // For now, document this as if it were a 'select'.
+ VisitSelect(P);
+ }
+
void VisitPlural(PluralPiece *P) { VisitSelect(P); }
void VisitDiff(DiffPiece *P) {
@@ -910,6 +938,47 @@ struct DiagTextDocPrinter : DiagTextVisitor<DiagTextDocPrinter> {
std::vector<std::string> &RST;
};
+struct DiagEnumPrinter : DiagTextVisitor<DiagEnumPrinter> {
+public:
+ using BaseTy = DiagTextVisitor<DiagEnumPrinter>;
+ using EnumeratorItem = std::pair<unsigned, std::string>;
+ using EnumeratorList = llvm::SmallVector<EnumeratorItem>;
+ using ResultTy = llvm::SmallVector<std::pair<std::string, EnumeratorList>>;
+
+ DiagEnumPrinter(DiagnosticTextBuilder &Builder, ResultTy &Result)
+ : BaseTy(Builder), Result(Result) {}
+
+ ResultTy &Result;
+
+ void VisitMulti(MultiPiece *P) {
+ for (auto *Child : P->Pieces)
+ Visit(Child);
+ }
+ void VisitText(TextPiece *P) {}
+ void VisitPlaceholder(PlaceholderPiece *P) {}
+ void VisitDiff(DiffPiece *P) {}
+ void VisitSelect(SelectPiece *P) {
+ for (auto *D : P->Options)
+ Visit(D);
+ }
+ void VisitPlural(PluralPiece *P) { VisitSelect(P); }
+ void VisitEnumSelect(EnumSelectPiece *P) {
+ assert(P->Options.size() == P->OptionEnumNames.size());
+
+ if (!P->EnumName.empty()) {
+ EnumeratorList List;
+
+ for (const auto &Tup : llvm::enumerate(P->OptionEnumNames))
+ if (!Tup.value().empty())
+ List.emplace_back(Tup.index(), Tup.value());
+
+ Result.emplace_back(P->EnumName, List);
+ }
+
+ VisitSelect(P);
+ }
+};
+
struct DiagTextPrinter : DiagTextVisitor<DiagTextPrinter> {
public:
using BaseTy = DiagTextVisitor<DiagTextPrinter>;
@@ -929,7 +998,7 @@ struct DiagTextPrinter : DiagTextVisitor<DiagTextPrinter> {
void VisitSelect(SelectPiece *P) {
Result += "%";
Result += getModifierName(P->ModKind);
- if (P->ModKind == MT_Select) {
+ if (P->ModKind == MT_Select || P->ModKind == MT_EnumSelect) {
Result += "{";
for (auto *D : P->Options) {
Visit(D);
@@ -958,6 +1027,13 @@ struct DiagTextPrinter : DiagTextVisitor<DiagTextPrinter> {
addInt(mapIndex(P->Index));
}
+ void VisitEnumSelect(EnumSelectPiece *P) {
+ // Print as if we are a 'select', which will result in the compiler just
+ // treating this like a normal select. This way we don't have to do any
+ // special work for the compiler to consume these.
+ VisitSelect(P);
+ }
+
void VisitDiff(DiffPiece *P) {
Result += "%diff{";
Visit(P->Parts[0]);
@@ -1019,11 +1095,12 @@ Piece *DiagnosticTextBuilder::DiagText::parseDiagText(StringRef &Text,
Text = Text.drop_front();
// Extract the (optional) modifier.
- size_t ModLength = Text.find_first_of("0123456789{");
+ size_t ModLength = Text.find_first_of("0123456789<{");
StringRef Modifier = Text.slice(0, ModLength);
Text = Text.slice(ModLength, StringRef::npos);
ModifierType ModType = StringSwitch<ModifierType>{Modifier}
.Case("select", MT_Select)
+ .Case("enum_select", MT_EnumSelect)
.Case("sub", MT_Sub)
.Case("diff", MT_Diff)
.Case("plural", MT_Plural)
@@ -1042,6 +1119,9 @@ Piece *DiagnosticTextBuilder::DiagText::parseDiagText(StringRef &Text,
Modifier);
};
+ if (ModType != MT_EnumSelect && Text[0] == '<')
+ Builder.PrintFatalError("modifier '<' syntax not valid with " + Modifier);
+
switch (ModType) {
case MT_Unknown:
Builder.PrintFatalError("Unknown modifier type: " + Modifier);
@@ -1058,6 +1138,55 @@ Piece *DiagnosticTextBuilder::DiagText::parseDiagText(StringRef &Text,
Parsed.push_back(Select);
continue;
}
+ case MT_EnumSelect: {
+ EnumSelectPiece *EnumSelect = New<EnumSelectPiece>();
+ if (Text[0] != '<')
+ Builder.PrintFatalError("expected '<' after " + Modifier);
+
+ Text = Text.drop_front(); // Drop '<'
+ size_t EnumNameLen = Text.find_first_of('>');
+ EnumSelect->EnumName = Text.slice(0, EnumNameLen);
+ Text = Text.slice(EnumNameLen, StringRef::npos);
+ ExpectAndConsume(">");
+
+ if (Text[0] != '{')
+ Builder.PrintFatalError("expected '{' after " + Modifier);
+
+ do {
+ Text = Text.drop_front(); // '{' or '|'
+
+ bool BracketsRequired = false;
+ if (Text[0] == '%') {
+ BracketsRequired = true;
+ Text = Text.drop_front(); // '%'
+ size_t OptionNameLen = Text.find_first_of("{");
+ EnumSelect->OptionEnumNames.push_back(Text.slice(0, OptionNameLen));
+ Text = Text.slice(OptionNameLen, StringRef::npos);
+ } else {
+ EnumSelect->OptionEnumNames.push_back({});
+ }
+
+ if (BracketsRequired)
+ ExpectAndConsume("{");
+ else if (Text.front() == '{') {
+ Text = Text.drop_front();
+ BracketsRequired = true;
+ }
+
+ EnumSelect->Options.push_back(
+ parseDiagText(Text, StopAt::PipeOrCloseBrace));
+
+ if (BracketsRequired)
+ ExpectAndConsume("}");
+
+ assert(!Text.empty() && "malformed %select");
+ } while (Text.front() == '|');
+
+ ExpectAndConsume("}");
+ EnumSelect->Index = parseModifier(Text);
+ Parsed.push_back(EnumSelect);
+ continue;
+ }
case MT_Plural: {
PluralPiece *Plural = New<PluralPiece>();
do {
@@ -1163,6 +1292,15 @@ DiagnosticTextBuilder::buildForDocumentation(StringRef Severity,
return Result;
}
+DiagEnumPrinter::ResultTy DiagnosticTextBuilder::buildForEnum(const Record *R) {
+ EvaluatingRecordGuard Guard(&EvaluatingRecord, R);
+ StringRef Text = R->getValueAsString("Summary");
+ DiagText D(*this, Text);
+ DiagEnumPrinter::ResultTy Result;
+ DiagEnumPrinter{*this, Result}.Visit(D.Root);
+ return Result;
+}
+
std::string DiagnosticTextBuilder::buildForDefinition(const Record *R) {
EvaluatingRecordGuard Guard(&EvaluatingRecord, R);
StringRef Text = R->getValueAsString("Summary");
@@ -1377,6 +1515,56 @@ static void verifyDiagnosticWording(const Record &Diag) {
// runs into odd situations like [[clang::warn_unused_result]],
// #pragma clang, or --unwindlib=libgcc.
}
+/// ClangDiagsEnumsEmitter - The top-level class emits .def files containing
+/// declarations of Clang diagnostic enums for selects.
+void clang::EmitClangDiagsEnums(const RecordKeeper &Records, raw_ostream &OS,
+ const std::string &Component) {
+ DiagnosticTextBuilder DiagTextBuilder(Records);
+ ArrayRef<const Record *> Diags =
+ Records.getAllDerivedDefinitions("Diagnostic");
+
+ llvm::SmallVector<std::pair<const Record *, std::string>> EnumerationNames;
+
+ for (const Record &R : make_pointee_range(Diags)) {
+ DiagEnumPrinter::ResultTy Enums = DiagTextBuilder.buildForEnum(&R);
+
+ for (auto &Enumeration : Enums) {
+ bool ShouldPrint =
+ Component.empty() || Component == R.getValueAsString("Component");
+
+ auto PreviousByName = llvm::find_if(EnumerationNames, [&](auto &Prev) {
+ return Prev.second == Enumeration.first;
+ });
+
+ if (PreviousByName != EnumerationNames.end()) {
+ PrintError(&R,
+ "Duplicate enumeration name '" + Enumeration.first + "'");
+ PrintNote(PreviousByName->first->getLoc(),
+ "Previous diagnostic is here");
+ }
+
+ EnumerationNames.emplace_back(&R, Enumeration.first);
+
+ if (ShouldPrint)
+ OS << "DIAG_ENUM(" << Enumeration.first << ")\n";
+
+ llvm::SmallVector<std::string> EnumeratorNames;
+ for (auto &Enumerator : Enumeration.second) {
+ if (llvm::find(EnumeratorNames, Enumerator.second) !=
+ EnumeratorNames.end())
+ PrintError(&R,
+ "Duplicate enumerator name '" + Enumerator.second + "'");
+ EnumeratorNames.push_back(Enumerator.second);
+
+ if (ShouldPrint)
+ OS << "DIAG_ENUM_ITEM(" << Enumerator.first << ", "
+ << Enumerator.second << ")\n";
+ }
+ if (ShouldPrint)
+ OS << "DIAG_ENUM_END()\n";
+ }
+ }
+}
/// ClangDiagsDefsEmitter - The top-level class emits .def files containing
/// declarations of Clang diagnostics.
diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp
index 6e2bd0c9f819b8..8b8eadbe7f7e54 100644
--- a/clang/utils/TableGen/TableGen.cpp
+++ b/clang/utils/TableGen/TableGen.cpp
@@ -48,6 +48,7 @@ enum ActionType {
GenClangBasicWriter,
GenClangBuiltins,
GenClangDiagsDefs,
+ GenClangDiagsEnums,
GenClangDiagGroups,
GenClangDiagsIndexName,
GenClangCommentNodes,
@@ -173,6 +174,8 @@ cl::opt<ActionType> Action(
"Generate clang builtins list"),
clEnumValN(GenClangDiagsDefs, "gen-clang-diags-defs",
"Generate Clang diagnostics definitions"),
+ clEnumValN(GenClangDiagsEnums, "gen-clang-diags-enums",
+ "Generate Clang diagnostic enums for selects"),
clEnumValN(GenClangDiagGroups, "gen-clang-diag-groups",
"Generate Clang diagnostic groups"),
clEnumValN(GenClangDiagsIndexName, "gen-clang-diags-index-name",
@@ -387,6 +390,9 @@ bool ClangTableGenMain(raw_ostream &OS, const RecordKeeper &Records) {
case GenClangDiagsDefs:
EmitClangDiagsDefs(Records, OS, ClangComponent);
break;
+ case GenClangDiagsEnums:
+ EmitClangDiagsEnums(Records, OS, ClangComponent);
+ break;
case GenClangDiagGroups:
EmitClangDiagGroups(Records, OS);
break;
diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h
index f7527ac535a870..0448c94de08e3d 100644
--- a/clang/utils/TableGen/TableGenBackends.h
+++ b/clang/utils/TableGen/TableGenBackends.h
@@ -89,6 +89,8 @@ void EmitClangBuiltins(const llvm::RecordKeeper &Records,
void EmitClangDiagsDefs(const llvm::RecordKeeper &Records,
llvm::raw_ostream &OS, const std::string &Component);
+void EmitClangDiagsEnums(const llvm::RecordKeeper &Records,
+ llvm::raw_ostream &OS, const std::string &Component);
void EmitClangDiagGroups(const llvm::RecordKeeper &Records,
llvm::raw_ostream &OS);
void EmitClangDiagsIndexName(const llvm::RecordKeeper &Records,
More information about the cfe-commits
mailing list