https://github.com/garima-gupta511 created 
https://github.com/llvm/llvm-project/pull/228401

[clang-tidy] readability-enum-initial-value: fix self-reference detection

Fix `AllowReferencedInitialValues` failing to recognize references to another 
enumerator in unscoped enums when the reference is implicitly converted to the 
enum's underlying type.

Add a regression test for this case.

---
LLVM already supports enumerators that explicitly refer to another enumerator 
through AllowReferencedInitialValues.
For example, the existing implementation supports:

  E_a,
  E_b,
  E_last = E_b,
};
The existing upstream tests also cover cases where the first enumerator is 
explicitly initialized and a later enumerator refers to an earlier one.

// OK: first-only + self-ref.
enum ERef2 {
  ERef2_a = 0,
  ERef2_b,
  ERef2_last = ERef2_b,
};
However, the following valid pattern was not handled correctly:

enum EFirstThenRefs {
  EFirstThenRefs_a = 0,
  EFirstThenRefs_b,
  EFirstThenRefs_c,
  EFirstThenRefs_first = EFirstThenRefs_a,
  EFirstThenRefs_last = EFirstThenRefs_c,
};
Here, the enum has an explicit first value, followed by implicitly initialized 
enumerators, and then two enumerators that explicitly reference earlier 
enumerators.
With AllowReferencedInitialValues enabled, these references should be treated 
as allowed references rather than as inconsistent explicit initializers.
This PR fixes the existing reference handling so that this pattern is 
recognized correctly.
The change does not introduce a new reference/alias option; it fixes the 
existing AllowReferencedInitialValues behavior for this case.

>From 8b1098ad0ea6e42f13aa2d0d42241581773e1e68 Mon Sep 17 00:00:00 2001
From: Garima Gupta <[email protected]>
Date: Fri, 18 Sep 2026 17:49:03 +0100
Subject: [PATCH] [clang-tidy] readability-enum-initial-value: fix
 self-reference detection

Fix `AllowReferencedInitialValues` failing to recognize references to another
enumerator in unscoped enums when the reference is implicitly converted to the
enum's underlying type.

Add a regression test for this case.
---
 .../readability/EnumInitialValueCheck.cpp     | 16 ++++++++++++----
 .../checks/readability/enum-initial-value.md  |  5 +++--
 .../enum-initial-value-allow-refs.cpp         | 19 +++++++++++++++++++
 3 files changed, 34 insertions(+), 6 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp 
b/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
index 620c5bece4ebad7..b95c7daa933a8be 100644
--- a/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
@@ -21,11 +21,19 @@ using namespace clang::ast_matchers;
 namespace clang::tidy::readability {
 
 /// Check if \p ECD is initialized by referencing another enumerator in the
-/// same enum (e.g., `last = first`).
+/// same enum (e.g., `last = first`). For unscoped enums, the reference may be
+/// wrapped in an implicit conversion to the enum's underlying type.
 static bool isSelfReference(const EnumConstantDecl *ECD) {
-  const auto *CE = dyn_cast_if_present<ConstantExpr>(ECD->getInitExpr());
-  const auto *DRE =
-      dyn_cast_if_present<DeclRefExpr>(CE ? CE->getSubExpr() : nullptr);
+  const Expr *Init = ECD->getInitExpr();
+  if (!Init)
+    return false;
+
+  if (const auto *CE = dyn_cast<ConstantExpr>(Init))
+    Init = CE->getSubExpr();
+
+  Init = Init->IgnoreImpCasts();
+
+  const auto *DRE = dyn_cast<DeclRefExpr>(Init);
   const auto *RefECD =
       dyn_cast_if_present<EnumConstantDecl>(DRE ? DRE->getDecl() : nullptr);
   return RefECD && RefECD->getDeclContext() == ECD->getDeclContext();
diff --git 
a/clang-tools-extra/docs/clang-tidy/checks/readability/enum-initial-value.md 
b/clang-tools-extra/docs/clang-tidy/checks/readability/enum-initial-value.md
index aec3af8c11eec23..d504e4bb86b4d07 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/enum-initial-value.md
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/enum-initial-value.md
@@ -94,8 +94,9 @@ enum G {
 ````{option} AllowReferencedInitialValues
 When `true`, enumerators initialized by referencing another enumerator
 in the same enum are allowed, and the remaining enumerators are checked for
-consistency. This implements the `INT09-C-EX1` exception from the CERT C
-Coding Standard.
+consistency. References to earlier enumerators are also recognized when they
+occur after implicitly initialized enumerators. This implements the
+`INT09-C-EX1` exception from the CERT C Coding Standard.
 Default is `false`.
 
 ```c++
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/enum-initial-value-allow-refs.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/enum-initial-value-allow-refs.cpp
index f66b75fec37100c..5f1b0e282816e00 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/readability/enum-initial-value-allow-refs.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/enum-initial-value-allow-refs.cpp
@@ -24,6 +24,25 @@ enum class ERefAll {
   ERefAll_last = ERefAll_b,
 };
 
+// OK: first-only + self-refs, where later self-references do not need to
+// immediately follow the enumerator they reference and do not require every
+// preceding enumerator to be explicitly initialized.
+enum EFirstThenRefs {
+  EFirstThenRefs_a = 0,
+  EFirstThenRefs_b,
+  EFirstThenRefs_c,
+  EFirstThenRefs_first = EFirstThenRefs_a,
+  EFirstThenRefs_last = EFirstThenRefs_c,
+};
+
+// OK: a self-reference may appear before later implicit enumerators.
+enum ERefThenImplicit {
+  ERefThenImplicit_a = 0,
+  ERefThenImplicit_alias = ERefThenImplicit_a,
+  ERefThenImplicit_b,
+  ERefThenImplicit_c,
+};
+
 // Error: literal duplicate (not a reference).
 enum class ERefErr {
   // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum 'ERefErr' 
are not consistent

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to