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->setLocation(Pattern->getLocation()); Enum->setBraceRange(Pattern->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
