[clang] [clang] improve diagnostics for enums (PR #204682)
Matthias Wippich via cfe-commits
cfe-commits at lists.llvm.org
Sat Jul 18 18:54:04 PDT 2026
https://github.com/Tsche updated https://github.com/llvm/llvm-project/pull/204682
>From b3c82970bebe87c557f17fbd23e4d177a16f0fcc Mon Sep 17 00:00:00 2001
From: Matthias Wippich <mfwippich at gmail.com>
Date: Thu, 18 Jun 2026 22:57:28 +0200
Subject: [PATCH 1/3] [clang] improve diagnostics for enums
---
clang/docs/ReleaseNotes.rst | 3 +++
clang/lib/AST/StmtPrinter.cpp | 15 +++++++++++++++
.../test/Misc/diag-template-diffing-cxx11.cpp | 2 +-
clang/test/SemaCXX/static-assert.cpp | 18 ++++++++++++++++++
4 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 7828135a6edbc..0d824f47764ca 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -646,6 +646,9 @@ Improvements to Clang's diagnostics
- Diagnostics for the C++11 range-based for statement now report the correct
iterator type in notes for invalid iterator types.
+- Clang now attempts to print enumerator names rather than C-style cast expressions
+ in more diagnostics.
+
Improvements to Clang's time-trace
----------------------------------
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 6c3294573e9d4..889592c47e962 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -1843,6 +1843,21 @@ void StmtPrinter::VisitMatrixElementExpr(MatrixElementExpr *Node) {
}
void StmtPrinter::VisitCStyleCastExpr(CStyleCastExpr *Node) {
+ // special case enums to avoid producing cast expressions when naming
+ // an enumerator would suffice
+ if (QualType T = Node->getType(); T->isEnumeralType()) {
+ const auto *IL = dyn_cast<IntegerLiteral>(Node->getSubExpr());
+ const auto *ED = T->getAsEnumDecl();
+ if (IL && ED) {
+ llvm::APInt Val = IL->getValue();
+ for (const EnumConstantDecl *ECD : ED->enumerators()) {
+ if (llvm::APInt::isSameValue(ECD->getInitVal(), Val)) {
+ ECD->printQualifiedName(OS, Policy);
+ return;
+ }
+ }
+ }
+ }
OS << '(';
Node->getTypeAsWritten().print(OS, Policy);
OS << ')';
diff --git a/clang/test/Misc/diag-template-diffing-cxx11.cpp b/clang/test/Misc/diag-template-diffing-cxx11.cpp
index 0b145475fe191..e543a3a132489 100644
--- a/clang/test/Misc/diag-template-diffing-cxx11.cpp
+++ b/clang/test/Misc/diag-template-diffing-cxx11.cpp
@@ -1451,7 +1451,7 @@ void run() {
D<X::X1>(VectorType<X::X2>());
}
// CHECK-ELIDE-NOTREE: error: no matching function for call to 'D'
-// CHECK-ELIDE-NOTREE: note: candidate function template not viable: no known conversion from 'VectorType<X::X2>' to 'const VectorType<(X)0>' for 1st argument
+// CHECK-ELIDE-NOTREE: note: candidate function template not viable: no known conversion from 'VectorType<X::X2>' to 'const VectorType<TypeAlias::X::X1>' for 1st argument
}
namespace TypeAlias2 {
diff --git a/clang/test/SemaCXX/static-assert.cpp b/clang/test/SemaCXX/static-assert.cpp
index 354016db36432..5ea42d4c99489 100644
--- a/clang/test/SemaCXX/static-assert.cpp
+++ b/clang/test/SemaCXX/static-assert.cpp
@@ -363,4 +363,22 @@ namespace Diagnostics {
// expected-note {{evaluates to '1 == 2'}}
static_assert(1 << 3 != 8, ""); // expected-error {{failed}} \
// expected-note {{evaluates to '8 != 8'}}
+ enum class TestEnum {
+ A = 0,
+ B = 1
+ };
+
+ template<TestEnum E> struct EnumTemplate {
+ static_assert(E == TestEnum::B, ""); // #enum-assert
+ };
+
+ EnumTemplate<TestEnum::A> test1;
+ // expected-error@#enum-assert {{static assertion failed due to requirement 'Diagnostics::TestEnum::A == Diagnostics::TestEnum::B': }} \
+ // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::TestEnum::A>' requested here}} \
+ // expected-note@#enum-assert {{evaluates to '0 == 1'}}
+
+ EnumTemplate<TestEnum(42)> test2;
+ // expected-error@#enum-assert {{static assertion failed due to requirement '(Diagnostics::TestEnum)42 == Diagnostics::TestEnum::B': }} \
+ // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<42>' requested here}} \
+ // expected-note@#enum-assert {{evaluates to '42 == 1'}}
}
>From f7eed91d87191e8b482766ecce5dd11f9a9e8c47 Mon Sep 17 00:00:00 2001
From: Matthias Wippich <mfwippich at gmail.com>
Date: Tue, 23 Jun 2026 23:11:34 +0200
Subject: [PATCH 2/3] add tests, move comment
---
clang/lib/AST/StmtPrinter.cpp | 5 ++-
clang/test/SemaCXX/static-assert.cpp | 66 +++++++++++++++++++++++-----
2 files changed, 59 insertions(+), 12 deletions(-)
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 889592c47e962..1b4af763a15bb 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -1843,9 +1843,10 @@ void StmtPrinter::VisitMatrixElementExpr(MatrixElementExpr *Node) {
}
void StmtPrinter::VisitCStyleCastExpr(CStyleCastExpr *Node) {
- // special case enums to avoid producing cast expressions when naming
- // an enumerator would suffice
if (QualType T = Node->getType(); T->isEnumeralType()) {
+ // special case enums to avoid producing cast expressions when naming
+ // an enumerator would suffice
+
const auto *IL = dyn_cast<IntegerLiteral>(Node->getSubExpr());
const auto *ED = T->getAsEnumDecl();
if (IL && ED) {
diff --git a/clang/test/SemaCXX/static-assert.cpp b/clang/test/SemaCXX/static-assert.cpp
index 5ea42d4c99489..b3c833e322dc4 100644
--- a/clang/test/SemaCXX/static-assert.cpp
+++ b/clang/test/SemaCXX/static-assert.cpp
@@ -368,17 +368,63 @@ namespace Diagnostics {
B = 1
};
- template<TestEnum E> struct EnumTemplate {
- static_assert(E == TestEnum::B, ""); // #enum-assert
+ enum class WithUnderlying : unsigned{
+ A = 0,
+ B = 1
+ };
+
+ enum TestEnumUnscoped {
+ TEST_A = 0,
+ TEST_B = 1,
+ TEST_C = 3
};
- EnumTemplate<TestEnum::A> test1;
- // expected-error@#enum-assert {{static assertion failed due to requirement 'Diagnostics::TestEnum::A == Diagnostics::TestEnum::B': }} \
- // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::TestEnum::A>' requested here}} \
- // expected-note@#enum-assert {{evaluates to '0 == 1'}}
+ enum WithUnderlyingUnscoped : unsigned {
+ TEST2_A = 0,
+ TEST2_B = 1
+ };
+
+ template<typename T, T E> struct EnumTemplate {
+ static_assert(E == (T)0, ""); // #enum-assert
+ };
- EnumTemplate<TestEnum(42)> test2;
- // expected-error@#enum-assert {{static assertion failed due to requirement '(Diagnostics::TestEnum)42 == Diagnostics::TestEnum::B': }} \
- // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<42>' requested here}} \
- // expected-note@#enum-assert {{evaluates to '42 == 1'}}
+ EnumTemplate<TestEnum, TestEnum::B> test1;
+ // expected-error@#enum-assert {{static assertion failed due to requirement 'Diagnostics::TestEnum::B == Diagnostics::TestEnum::A': }} \
+ // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::TestEnum, Diagnostics::TestEnum::B>' requested here}} \
+ // expected-note@#enum-assert {{evaluates to '1 == 0'}}
+
+ EnumTemplate<TestEnum, TestEnum(42)> test2;
+ // expected-error@#enum-assert {{static assertion failed due to requirement '(Diagnostics::TestEnum)42 == Diagnostics::TestEnum::A': }} \
+ // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::TestEnum, (Diagnostics::TestEnum)42>' requested here}} \
+ // expected-note@#enum-assert {{evaluates to '42 == 0'}}
+
+ EnumTemplate<WithUnderlying, WithUnderlying::B> test3;
+ // expected-error@#enum-assert {{static assertion failed due to requirement 'Diagnostics::WithUnderlying::B == Diagnostics::WithUnderlying::A': }} \
+ // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::WithUnderlying, Diagnostics::WithUnderlying::B>' requested here}} \
+ // expected-note@#enum-assert {{evaluates to '1 == 0'}}
+
+ EnumTemplate<WithUnderlying, WithUnderlying(42)> test6;
+ // expected-error@#enum-assert {{static assertion failed due to requirement '(Diagnostics::WithUnderlying)42 == Diagnostics::WithUnderlying::A': }} \
+ // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::WithUnderlying, (Diagnostics::WithUnderlying)42>' requested here}} \
+ // expected-note@#enum-assert {{evaluates to '42 == 0'}}
+
+ EnumTemplate<TestEnumUnscoped, TestEnumUnscoped::TEST_B> test4;
+ // expected-error@#enum-assert {{static assertion failed due to requirement 'Diagnostics::TEST_B == Diagnostics::TEST_A': }} \
+ // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::TestEnumUnscoped, Diagnostics::TEST_B>' requested here}} \
+ // expected-note@#enum-assert {{evaluates to '1 == 0'}}
+
+ EnumTemplate<TestEnumUnscoped, TestEnumUnscoped(2)> test7;
+ // expected-error@#enum-assert {{static assertion failed due to requirement '(Diagnostics::TestEnumUnscoped)2 == Diagnostics::TEST_A': }} \
+ // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::TestEnumUnscoped, (Diagnostics::TestEnumUnscoped)2>' requested here}} \
+ // expected-note@#enum-assert {{evaluates to '2 == 0'}}
+
+ EnumTemplate<WithUnderlyingUnscoped, WithUnderlyingUnscoped::TEST2_B> test5;
+ // expected-error@#enum-assert {{static assertion failed due to requirement 'Diagnostics::TEST2_B == Diagnostics::TEST2_A': }} \
+ // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::WithUnderlyingUnscoped, Diagnostics::TEST2_B>' requested here}} \
+ // expected-note@#enum-assert {{evaluates to '1 == 0'}}
+
+ EnumTemplate<WithUnderlyingUnscoped, WithUnderlyingUnscoped(42)> test8;
+ // expected-error@#enum-assert {{static assertion failed due to requirement '(Diagnostics::WithUnderlyingUnscoped)42 == Diagnostics::TEST2_A': }} \
+ // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::WithUnderlyingUnscoped, (Diagnostics::WithUnderlyingUnscoped)42>' requested here}} \
+ // expected-note@#enum-assert {{evaluates to '42 == 0'}}
}
>From 647ebe43caa63054e235f492aa184a7b308cc61d Mon Sep 17 00:00:00 2001
From: Matthias Wippich <mfwippich at gmail.com>
Date: Sun, 19 Jul 2026 03:48:59 +0200
Subject: [PATCH 3/3] disable enum pretty printing in ast-print mode
---
clang/include/clang/AST/PrettyPrinter.h | 7 ++++++-
clang/lib/AST/StmtPrinter.cpp | 14 ++++++++------
clang/lib/Frontend/ASTConsumers.cpp | 1 +
clang/test/AST/ast-print-enum-decl.c | 3 +++
clang/test/SemaCXX/static-assert.cpp | 14 +++++++-------
5 files changed, 25 insertions(+), 14 deletions(-)
diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h
index f95d54c0f05b9..c6555215ce20a 100644
--- a/clang/include/clang/AST/PrettyPrinter.h
+++ b/clang/include/clang/AST/PrettyPrinter.h
@@ -93,7 +93,7 @@ struct PrintingPolicy {
PrintAsCanonical(false), PrintInjectedClassNameWithArguments(true),
UsePreferredNames(true), AlwaysIncludeTypeForTemplateArgument(false),
CleanUglifiedParameters(false), EntireContentsOfLargeArray(true),
- UseEnumerators(true), UseHLSLTypes(LO.HLSL),
+ PrettyEnums(true), UseEnumerators(true), UseHLSLTypes(LO.HLSL),
SuppressDeclAttributes(false), SuppressLambdaBody(false) {}
/// Adjust this printing policy for cases where it's known that we're
@@ -357,6 +357,11 @@ struct PrintingPolicy {
LLVM_PREFERRED_TYPE(bool)
unsigned EntireContentsOfLargeArray : 1;
+ /// Whether to print enumerators with a matching enumerator name or via cast
+ // of an integer.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned PrettyEnums : 1;
+
/// Whether to print enumerator non-type template parameters with a matching
/// enumerator name or via cast of an integer.
LLVM_PREFERRED_TYPE(bool)
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 1b4af763a15bb..0e42fab3965b7 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -1843,7 +1843,7 @@ void StmtPrinter::VisitMatrixElementExpr(MatrixElementExpr *Node) {
}
void StmtPrinter::VisitCStyleCastExpr(CStyleCastExpr *Node) {
- if (QualType T = Node->getType(); T->isEnumeralType()) {
+ if (QualType T = Node->getType(); Policy.PrettyEnums && T->isEnumeralType()) {
// special case enums to avoid producing cast expressions when naming
// an enumerator would suffice
@@ -1851,11 +1851,13 @@ void StmtPrinter::VisitCStyleCastExpr(CStyleCastExpr *Node) {
const auto *ED = T->getAsEnumDecl();
if (IL && ED) {
llvm::APInt Val = IL->getValue();
- for (const EnumConstantDecl *ECD : ED->enumerators()) {
- if (llvm::APInt::isSameValue(ECD->getInitVal(), Val)) {
- ECD->printQualifiedName(OS, Policy);
- return;
- }
+ const auto ECD =
+ llvm::find_if(ED->enumerators(), [&](const EnumConstantDecl *ECD) {
+ return llvm::APInt::isSameValue(ECD->getInitVal(), Val);
+ });
+ if (ECD != ED->enumerator_end()) {
+ ECD->printQualifiedName(OS, Policy);
+ return;
}
}
}
diff --git a/clang/lib/Frontend/ASTConsumers.cpp b/clang/lib/Frontend/ASTConsumers.cpp
index 67c8761511e0c..74b4768a7aaca 100644
--- a/clang/lib/Frontend/ASTConsumers.cpp
+++ b/clang/lib/Frontend/ASTConsumers.cpp
@@ -98,6 +98,7 @@ namespace {
} else if (OutputKind == Print) {
PrintingPolicy Policy(D->getASTContext().getLangOpts());
Policy.IncludeTagDefinition = true;
+ Policy.PrettyEnums = false;
D->print(Out, Policy, /*Indentation=*/0, /*PrintInstantiation=*/true);
} else if (OutputKind != None) {
D->dump(Out, OutputKind == DumpFull, OutputFormat);
diff --git a/clang/test/AST/ast-print-enum-decl.c b/clang/test/AST/ast-print-enum-decl.c
index 411467348fb11..f88af682a00f5 100644
--- a/clang/test/AST/ast-print-enum-decl.c
+++ b/clang/test/AST/ast-print-enum-decl.c
@@ -108,3 +108,6 @@ enum fixedEnum : int { fixedEnumerator };
// PRINT-LABEL: enum fixedEnum : int {
// PRINT-NEXT: fixedEnumerator
// PRINT-NEXT: };
+
+enum fixedEnum fixedEnumVar = (enum fixedEnum)0;
+//PRINT-LABEL: enum fixedEnum fixedEnumVar = (enum fixedEnum)0;
diff --git a/clang/test/SemaCXX/static-assert.cpp b/clang/test/SemaCXX/static-assert.cpp
index b3c833e322dc4..5ad6c63bbf321 100644
--- a/clang/test/SemaCXX/static-assert.cpp
+++ b/clang/test/SemaCXX/static-assert.cpp
@@ -395,7 +395,7 @@ namespace Diagnostics {
EnumTemplate<TestEnum, TestEnum(42)> test2;
// expected-error@#enum-assert {{static assertion failed due to requirement '(Diagnostics::TestEnum)42 == Diagnostics::TestEnum::A': }} \
- // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::TestEnum, (Diagnostics::TestEnum)42>' requested here}} \
+ // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::TestEnum, 42>' requested here}} \
// expected-note@#enum-assert {{evaluates to '42 == 0'}}
EnumTemplate<WithUnderlying, WithUnderlying::B> test3;
@@ -404,8 +404,8 @@ namespace Diagnostics {
// expected-note@#enum-assert {{evaluates to '1 == 0'}}
EnumTemplate<WithUnderlying, WithUnderlying(42)> test6;
- // expected-error@#enum-assert {{static assertion failed due to requirement '(Diagnostics::WithUnderlying)42 == Diagnostics::WithUnderlying::A': }} \
- // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::WithUnderlying, (Diagnostics::WithUnderlying)42>' requested here}} \
+ // expected-error@#enum-assert {{static assertion failed due to requirement '(Diagnostics::WithUnderlying)42U == Diagnostics::WithUnderlying::A': }} \
+ // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::WithUnderlying, 42>' requested here}} \
// expected-note@#enum-assert {{evaluates to '42 == 0'}}
EnumTemplate<TestEnumUnscoped, TestEnumUnscoped::TEST_B> test4;
@@ -414,8 +414,8 @@ namespace Diagnostics {
// expected-note@#enum-assert {{evaluates to '1 == 0'}}
EnumTemplate<TestEnumUnscoped, TestEnumUnscoped(2)> test7;
- // expected-error@#enum-assert {{static assertion failed due to requirement '(Diagnostics::TestEnumUnscoped)2 == Diagnostics::TEST_A': }} \
- // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::TestEnumUnscoped, (Diagnostics::TestEnumUnscoped)2>' requested here}} \
+ // expected-error@#enum-assert {{static assertion failed due to requirement '(Diagnostics::TestEnumUnscoped)2U == Diagnostics::TEST_A': }} \
+ // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::TestEnumUnscoped, 2>' requested here}} \
// expected-note@#enum-assert {{evaluates to '2 == 0'}}
EnumTemplate<WithUnderlyingUnscoped, WithUnderlyingUnscoped::TEST2_B> test5;
@@ -424,7 +424,7 @@ namespace Diagnostics {
// expected-note@#enum-assert {{evaluates to '1 == 0'}}
EnumTemplate<WithUnderlyingUnscoped, WithUnderlyingUnscoped(42)> test8;
- // expected-error@#enum-assert {{static assertion failed due to requirement '(Diagnostics::WithUnderlyingUnscoped)42 == Diagnostics::TEST2_A': }} \
- // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::WithUnderlyingUnscoped, (Diagnostics::WithUnderlyingUnscoped)42>' requested here}} \
+ // expected-error@#enum-assert {{static assertion failed due to requirement '(Diagnostics::WithUnderlyingUnscoped)42U == Diagnostics::TEST2_A': }} \
+ // expected-note at -1 {{in instantiation of template class 'Diagnostics::EnumTemplate<Diagnostics::WithUnderlyingUnscoped, 42>' requested here}} \
// expected-note@#enum-assert {{evaluates to '42 == 0'}}
}
More information about the cfe-commits
mailing list