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
