https://github.com/kumarak updated 
https://github.com/llvm/llvm-project/pull/210559

>From b48015b4450d5bda1531bad892ce910a4acf5c30 Mon Sep 17 00:00:00 2001
From: AkshayK <[email protected]>
Date: Sat, 18 Jul 2026 14:05:56 -0400
Subject: [PATCH] [Clang] Preserve the brace range of instantiated enum
 definitions

The parser stamps a TagDecl's brace range via ActOnTagFinishDefinition,
which template instantiation does not go through, and the BraceRange
parameter that InstantiateEnumDefinition passes to ActOnEnumBody is
unused. InstantiateClass copies the pattern's brace range for the class
itself, but instantiated member and local enum definitions were left
with an invalid brace range, so their source range ended at the enum
name (or enum-base) and excluded their own enumerators, breaking
range-based tooling.

Copy the pattern's brace range in InstantiateEnumDefinition, next to the
existing setLocation call. Opaque enum declarations whose definitions
are not instantiated are unaffected and keep an invalid brace range.
---
 clang/docs/ReleaseNotes.md                    |  2 +
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  3 +-
 clang/test/AST/ast-dump-decl.cpp              |  8 ++--
 .../AST/ast-dump-enum-instantiation-range.cpp | 40 +++++++++++++++++++
 clang/unittests/AST/SourceLocationTest.cpp    | 24 +++++++++++
 5 files changed, 72 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/AST/ast-dump-enum-instantiation-range.cpp

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a39bc2dfc5b6d..4f22a08ef3310 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -323,6 +323,8 @@ latest release, please see the [Clang Web 
Site](https://clang.llvm.org) or the
 - Fixed a non-deterministic ordering of unused local typedefs that made
   serialized PCH/AST files and `-Wunused-local-typedef` diagnostics
   non-reproducible across runs. (#GH209639)
+- Instantiated enum definitions now preserve the pattern's brace range, so 
their
+  source ranges include their enumerators.
 
 #### Miscellaneous Bug Fixes
 
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 2a711db70280e..bc45d01397aea 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2249,8 +2249,9 @@ void TemplateDeclInstantiator::InstantiateEnumDefinition(
     EnumDecl *Enum, EnumDecl *Pattern) {
   Enum->startDefinition();
 
-  // Update the location to refer to the definition.
+  // Use the definition's source locations.
   Enum->setLocation(Pattern->getLocation());
+  Enum->setBraceRange(Pattern->getBraceRange());
 
   SmallVector<Decl*, 4> Enumerators;
 
diff --git a/clang/test/AST/ast-dump-decl.cpp b/clang/test/AST/ast-dump-decl.cpp
index ef5e0a55e431e..b2964d132762c 100644
--- a/clang/test/AST/ast-dump-decl.cpp
+++ b/clang/test/AST/ast-dump-decl.cpp
@@ -529,12 +529,12 @@ namespace testClassTemplateDecl {
 // CHECK:      ClassTemplateSpecializationDecl 0x{{.+}} 
<{{.+}}:[[@LINE-29]]:3, col:65> col:19 struct 
TestClassTemplateWithScopedMemberEnum definition external-linkage 
instantiated_from 0x{{.+}} explicit_instantiation_definition
 // CHECK:      |-TemplateArgument type 'unsigned int'
 // CHECK-NEXT: | `-BuiltinType 0x{{.+}} 'unsigned int'
-// CHECK:      |-EnumDecl 0x{{.+}} <line:[[@LINE-38]]:5, col:21> col:16 class 
E1 'unsigned int' instantiated_from 0x[[#SCOPED_MEMBER_ENUM_E1]] 
external-linkage{{$}}
+// CHECK:      |-EnumDecl 0x{{.+}} <line:[[@LINE-38]]:5, col:36> col:16 class 
E1 'unsigned int' instantiated_from 0x[[#SCOPED_MEMBER_ENUM_E1]] 
external-linkage{{$}}
 // CHECK-NEXT: | |-EnumConstantDecl 0x{{.+}} <col:25> col:25 A 
'testClassTemplateDecl::TestClassTemplateWithScopedMemberEnum<unsigned int>::E1'
 // CHECK-NEXT: | |-EnumConstantDecl 0x{{.+}} <col:28> col:28 B 
'testClassTemplateDecl::TestClassTemplateWithScopedMemberEnum<unsigned int>::E1'
 // CHECK-NEXT: | |-EnumConstantDecl 0x{{.+}} <col:31> col:31 C 
'testClassTemplateDecl::TestClassTemplateWithScopedMemberEnum<unsigned int>::E1'
 // CHECK-NEXT: | `-EnumConstantDecl 0x{{.+}} <col:34> col:34 D 
'testClassTemplateDecl::TestClassTemplateWithScopedMemberEnum<unsigned int>::E1'
-// CHECK-NEXT: |-EnumDecl 0x{{.+}} <line:[[@LINE-42]]:5, col:21> col:16 class 
E2 'int' instantiated_from 0x[[#SCOPED_MEMBER_ENUM_E2]] external-linkage{{$}}
+// CHECK-NEXT: |-EnumDecl 0x{{.+}} <line:[[@LINE-42]]:5, col:38> col:16 class 
E2 'int' instantiated_from 0x[[#SCOPED_MEMBER_ENUM_E2]] external-linkage{{$}}
 // CHECK-NEXT: | |-EnumConstantDecl 0x{{.+}} <col:27> col:27 A 
'testClassTemplateDecl::TestClassTemplateWithScopedMemberEnum<unsigned int>::E2'
 // CHECK-NEXT: | |-EnumConstantDecl 0x{{.+}} <col:30> col:30 B 
'testClassTemplateDecl::TestClassTemplateWithScopedMemberEnum<unsigned int>::E2'
 // CHECK-NEXT: | |-EnumConstantDecl 0x{{.+}} <col:33> col:33 C 
'testClassTemplateDecl::TestClassTemplateWithScopedMemberEnum<unsigned int>::E2'
@@ -578,12 +578,12 @@ namespace testClassTemplateDecl {
 // CHECK:      ClassTemplateSpecializationDecl 0x{{.+}} 
<{{.+}}:[[@LINE-22]]:3, col:67> col:19 referenced struct 
TestClassTemplateWithUnscopedMemberEnum definition external-linkage 
instantiated_from 0x{{.+}} explicit_instantiation_definition
 // CHECK:      |-TemplateArgument type 'unsigned int'
 // CHECK-NEXT: | `-BuiltinType 0x{{.+}} 'unsigned int'
-// CHECK:      |-EnumDecl 0x{{.+}} <line:[[@LINE-31]]:5, col:15> col:10 E1 
'unsigned int' instantiated_from 0x[[#UNSCOPED_MEMBER_ENUM_E1]] 
external-linkage{{$}}
+// CHECK:      |-EnumDecl 0x{{.+}} <line:[[@LINE-31]]:5, col:42> col:10 E1 
'unsigned int' instantiated_from 0x[[#UNSCOPED_MEMBER_ENUM_E1]] 
external-linkage{{$}}
 // CHECK-NEXT: | |-EnumConstantDecl 0x{{.+}} <col:19> col:19 E1_A 
'testClassTemplateDecl::TestClassTemplateWithUnscopedMemberEnum<unsigned 
int>::E1'
 // CHECK-NEXT: | |-EnumConstantDecl 0x{{.+}} <col:25> col:25 E1_B 
'testClassTemplateDecl::TestClassTemplateWithUnscopedMemberEnum<unsigned 
int>::E1'
 // CHECK-NEXT: | |-EnumConstantDecl 0x{{.+}} <col:31> col:31 E1_C 
'testClassTemplateDecl::TestClassTemplateWithUnscopedMemberEnum<unsigned 
int>::E1'
 // CHECK-NEXT: | `-EnumConstantDecl 0x{{.+}} <col:37> col:37 E1_D 
'testClassTemplateDecl::TestClassTemplateWithUnscopedMemberEnum<unsigned 
int>::E1'
-// CHECK-NEXT: |-EnumDecl 0x{{.+}} <line:[[@LINE-35]]:5, col:15> col:10 E2 
'int' instantiated_from 0x[[#UNSCOPED_MEMBER_ENUM_E2]] external-linkage{{$}}
+// CHECK-NEXT: |-EnumDecl 0x{{.+}} <line:[[@LINE-35]]:5, col:44> col:10 E2 
'int' instantiated_from 0x[[#UNSCOPED_MEMBER_ENUM_E2]] external-linkage{{$}}
 // CHECK-NEXT: | |-EnumConstantDecl 0x{{.+}} <col:21> col:21 E2_A 
'testClassTemplateDecl::TestClassTemplateWithUnscopedMemberEnum<unsigned 
int>::E2'
 // CHECK-NEXT: | |-EnumConstantDecl 0x{{.+}} <col:27> col:27 E2_B 
'testClassTemplateDecl::TestClassTemplateWithUnscopedMemberEnum<unsigned 
int>::E2'
 // CHECK-NEXT: | |-EnumConstantDecl 0x{{.+}} <col:33> col:33 E2_C 
'testClassTemplateDecl::TestClassTemplateWithUnscopedMemberEnum<unsigned 
int>::E2'
diff --git a/clang/test/AST/ast-dump-enum-instantiation-range.cpp 
b/clang/test/AST/ast-dump-enum-instantiation-range.cpp
new file mode 100644
index 0000000000000..9ee07a8515b69
--- /dev/null
+++ b/clang/test/AST/ast-dump-enum-instantiation-range.cpp
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 -std=c++17 -ast-dump %s | FileCheck %s
+
+// Instantiated enum ranges must include their enumerators.
+
+template <typename T> struct A {
+  enum E { X = 1, Y = 2 };
+  enum class S : int { P, Q };
+  enum class U : int; // Remains undefined.
+};
+
+A<int> a;
+A<int>::S s = A<int>::S::P; // Instantiate S's definition.
+
+template <typename T> int f() {
+  enum L { M = sizeof(T) };
+  return M;
+}
+int force_f = f<char>();
+
+// Pattern enums.
+// CHECK: EnumDecl {{.*}} <line:[[@LINE-15]]:3, col:25> col:8 E
+// CHECK: EnumDecl {{.*}} <line:[[@LINE-15]]:3, col:29> col:14 class S 'int'
+// CHECK: EnumDecl {{.*}} <line:[[@LINE-15]]:3, col:18> col:14 class U 'int'
+
+// Instantiated member enums.
+// CHECK: ClassTemplateSpecializationDecl {{.*}} struct A definition
+// CHECK: EnumDecl {{.*}} <line:[[@LINE-21]]:3, col:25> col:8 E 
instantiated_from
+// CHECK: EnumConstantDecl {{.*}} <col:12, col:16> col:12 X 'A<int>::E'
+// CHECK: EnumConstantDecl {{.*}} <col:19, col:23> col:19 Y 'A<int>::E'
+// CHECK: EnumDecl {{.*}} <line:[[@LINE-23]]:3, col:29> col:14 referenced 
class S 'int' instantiated_from
+// CHECK: EnumConstantDecl {{.*}} <col:24> col:24 referenced P 'A<int>::S'
+// CHECK: EnumConstantDecl {{.*}} <col:27> col:27 Q 'A<int>::S'
+
+// The opaque enum has no brace range.
+// CHECK: EnumDecl {{.*}} <line:[[@LINE-27]]:3, col:18> col:14 class U 'int' 
instantiated_from
+
+// Instantiated local enum.
+// CHECK: FunctionDecl {{.*}} used f 'int ()' implicit_instantiation 
instantiated_from
+// CHECK: EnumDecl {{.*}} <col:3, col:26> col:8 L instantiated_from
+// CHECK: EnumConstantDecl {{.*}} <col:12, col:24> col:12 referenced M 'L'
diff --git a/clang/unittests/AST/SourceLocationTest.cpp 
b/clang/unittests/AST/SourceLocationTest.cpp
index 7bd0de04b2582..3170786163c1b 100644
--- a/clang/unittests/AST/SourceLocationTest.cpp
+++ b/clang/unittests/AST/SourceLocationTest.cpp
@@ -1129,4 +1129,28 @@ template <int X> requires NS::CCC<X> int z = X;
   EXPECT_TRUE(Verifier.match(Code2, varTemplateDecl(hasName("z")), 
Lang_CXX20));
 }
 
+TEST(EnumDecl, InstantiatedMemberEnumRange) {
+  // The range includes the enumerators.
+  RangeVerifier<EnumDecl> Verifier;
+  Verifier.expectRange(2, 3, 2, 25);
+  EXPECT_TRUE(Verifier.match("template <typename T> struct A {\n"
+                             "  enum E { X = 1, Y = 2 };\n"
+                             "};\n"
+                             "A<int> a;\n",
+                             enumDecl(hasName("E"), isInstantiated()),
+                             Lang_CXX11));
+}
+
+TEST(EnumDecl, InstantiatedScopedMemberEnumRange) {
+  // Referencing an enumerator instantiates the full range.
+  RangeVerifier<EnumDecl> Verifier;
+  Verifier.expectRange(2, 3, 2, 29);
+  EXPECT_TRUE(Verifier.match("template <typename T> struct A {\n"
+                             "  enum class S : int { P, Q };\n"
+                             "};\n"
+                             "auto s = A<int>::S::P;\n",
+                             enumDecl(hasName("S"), isInstantiated()),
+                             Lang_CXX11));
+}
+
 } // end namespace

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

Reply via email to