llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Akshay K (kumarak)

<details>
<summary>Changes</summary>

## Summary
Preserve the brace range when instantiating enum definitions from templates.

Previously, an instantiated enum definition had an invalid brace range. Its 
source range, therefore, ended before the enum body and did not include its 
enumerators, which produced incomplete ranges for AST consumers and range-based 
tooling.

## Cause
The parser records a `TagDecl` brace range through 
`ActOnTagFinishDefinition()`. Template instantiation does not use that parser 
callback.

`InstantiateEnumDefinition()` updated the instantiated enum's location but did 
not copy the pattern's brace range. This differed from class-template 
instantiation, which preserves the pattern's brace range.

## Fix
Copy the definition pattern's brace range in `InstantiateEnumDefinition()` 
alongside its source location:

  ```cpp
  Enum-&gt;setLocation(Pattern-&gt;getLocation());
  Enum-&gt;setBraceRange(Pattern-&gt;getBraceRange());
```

Opaque enum declarations remain unaffected because their definitions are not 
instantiated, and they have no brace range.

## Test
  Added coverage for:
  - Unscoped member enum definitions instantiated with their class.
  - Scoped member enum definitions are instantiated lazily.
  - Local enum definitions instantiated in function templates.
  - EnumDecl::getSourceRange() including the complete instantiated enum body.

---
Full diff: https://github.com/llvm/llvm-project/pull/210559.diff


5 Files Affected:

- (modified) clang/docs/ReleaseNotes.md (+2) 
- (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+2-1) 
- (modified) clang/test/AST/ast-dump-decl.cpp (+4-4) 
- (added) clang/test/AST/ast-dump-enum-instantiation-range.cpp (+40) 
- (modified) clang/unittests/AST/SourceLocationTest.cpp (+24) 


``````````diff
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

``````````

</details>


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

Reply via email to