https://github.com/frederic-tingaud-sonarsource updated https://github.com/llvm/llvm-project/pull/156011
>From 86f668015b087134c6f41ecd55ac4795652fa6bd Mon Sep 17 00:00:00 2001 From: Fred Tingaud <[email protected]> Date: Mon, 25 Aug 2025 20:07:04 +0200 Subject: [PATCH 1/2] [clang][AST] Fix source range of class template implicit instantiations. --- .../checkers/hicpp/exception-baseclass.cpp | 12 +-- clang/lib/AST/DeclTemplate.cpp | 7 +- clang/test/AST/ast-dump-decl.cpp | 8 +- ...penmp-begin-declare-variant_template_3.cpp | 4 +- .../test/AST/ast-dump-template-decls-json.cpp | 4 +- clang/test/AST/ast-dump-template-redecl.cpp | 88 +++++++++++++++++++ clang/test/AST/ast-dump-templates.cpp | 27 +++--- .../ParserHLSL/hlsl_resource_class_attr.hlsl | 2 +- 8 files changed, 121 insertions(+), 31 deletions(-) create mode 100644 clang/test/AST/ast-dump-template-redecl.cpp diff --git a/clang-tools-extra/test/clang-tidy/checkers/hicpp/exception-baseclass.cpp b/clang-tools-extra/test/clang-tidy/checkers/hicpp/exception-baseclass.cpp index b5e405a691848..c682cb92f87d0 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/hicpp/exception-baseclass.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/hicpp/exception-baseclass.cpp @@ -109,15 +109,15 @@ template <typename T> void ThrowException() { throw T(); } // CHECK-NOTES: [[@LINE-1]]:31: warning: throwing an exception whose type 'bad_generic_exception<int>' is not derived from 'std::exception' // CHECK-NOTES: [[@LINE-2]]:31: note: type 'bad_generic_exception<int>' is a template instantiation of 'T' -// CHECK-NOTES: [[@LINE+25]]:1: note: type defined here +// CHECK-NOTES: [[@LINE+26]]:1: note: type defined here // CHECK-NOTES: [[@LINE-5]]:31: warning: throwing an exception whose type 'bad_generic_exception<std::exception>' is not derived from 'std::exception' // CHECK-NOTES: [[@LINE-6]]:31: note: type 'bad_generic_exception<std::exception>' is a template instantiation of 'T' -// CHECK-NOTES: [[@LINE+21]]:1: note: type defined here +// CHECK-NOTES: [[@LINE+22]]:1: note: type defined here // CHECK-NOTES: [[@LINE-9]]:31: warning: throwing an exception whose type 'exotic_exception<non_derived_exception>' is not derived from 'std::exception' // CHECK-NOTES: [[@LINE-10]]:31: note: type 'exotic_exception<non_derived_exception>' is a template instantiation of 'T' -// CHECK-NOTES: [[@LINE+20]]:1: note: type defined here +// CHECK-NOTES: [[@LINE+21]]:1: note: type defined here // CHECK-NOTES: [[@LINE-13]]:31: warning: throwing an exception whose type 'int' is not derived from 'std::exception' // CHECK-NOTES: [[@LINE-14]]:31: note: type 'int' is a template instantiation of 'T' @@ -158,16 +158,16 @@ void generic_exceptions() { throw bad_generic_exception<int>(); // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'bad_generic_exception<int>' is not derived from 'std::exception' - // CHECK-NOTES: [[@LINE-24]]:1: note: type defined here + // CHECK-NOTES: [[@LINE-23]]:1: note: type defined here throw bad_generic_exception<std::exception>(); // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'bad_generic_exception<std::exception>' is not derived from 'std::exception' - // CHECK-NOTES: [[@LINE-27]]:1: note: type defined here + // CHECK-NOTES: [[@LINE-26]]:1: note: type defined here THROW_EXCEPTION(bad_generic_exception<int>); THROW_EXCEPTION(bad_generic_exception<std::exception>); throw exotic_exception<non_derived_exception>(); // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'exotic_exception<non_derived_exception>' is not derived from 'std::exception' - // CHECK-NOTES: [[@LINE-30]]:1: note: type defined here + // CHECK-NOTES: [[@LINE-29]]:1: note: type defined here THROW_EXCEPTION(exotic_exception<non_derived_exception>); throw exotic_exception<derived_exception>(); // Ok diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 3162857aac5d0..01cc33b795308 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -1050,7 +1050,12 @@ ClassTemplateSpecializationDecl::getSourceRange() const { if (const auto *CTPSD = dyn_cast<ClassTemplatePartialSpecializationDecl *>(Pattern)) return CTPSD->getSourceRange(); - return cast<ClassTemplateDecl *>(Pattern)->getSourceRange(); + + const auto *CTD = cast<ClassTemplateDecl *>(Pattern); + if (CTD->getTemplatedDecl()->hasDefinition()) + return CTD->getTemplatedDecl()->getDefinition()->getSourceRange(); + + return CTD->getSourceRange(); } case TSK_ExplicitSpecialization: { SourceRange Range = CXXRecordDecl::getSourceRange(); diff --git a/clang/test/AST/ast-dump-decl.cpp b/clang/test/AST/ast-dump-decl.cpp index afb507833d869..54723b01d5a95 100644 --- a/clang/test/AST/ast-dump-decl.cpp +++ b/clang/test/AST/ast-dump-decl.cpp @@ -334,7 +334,7 @@ namespace testClassTemplateDecl { // CHECK-NEXT: | |-CXXDestructorDecl 0x[[#%x,TEMPLATE_DESTRUCTOR_DECL:]] <line:[[@LINE-50]]:5, col:24> col:5 ~TestClassTemplate<T> 'void ()' not_selected{{$}} // CHECK-NEXT: | |-CXXMethodDecl 0x[[#%x,TEMPLATE_METHOD_DECL:]] <line:[[@LINE-50]]:5, col:11> col:9 j 'int ()'{{$}} // CHECK-NEXT: | `-FieldDecl 0x{{.+}} <line:[[@LINE-50]]:5, col:9> col:9 i 'int'{{$}} -// CHECK-NEXT: |-ClassTemplateSpecializationDecl 0x{{.+}} <line:[[@LINE-56]]:3, line:[[@LINE-50]]:3> line:[[@LINE-56]]:30 class TestClassTemplate definition implicit_instantiation{{$}} +// CHECK-NEXT: |-ClassTemplateSpecializationDecl 0x{{.+}} <line:[[@LINE-56]]:24, line:[[@LINE-50]]:3> line:[[@LINE-56]]:30 class TestClassTemplate definition implicit_instantiation{{$}} // CHECK-NEXT: | |-DefinitionData standard_layout has_user_declared_ctor can_const_default_init{{$}} // CHECK-NEXT: | | |-DefaultConstructor exists non_trivial user_provided{{$}} // CHECK-NEXT: | | |-CopyConstructor simple trivial has_const_param implicit_has_const_param{{$}} @@ -518,7 +518,7 @@ namespace testClassTemplateDecl { // CHECK-NEXT: | |-EnumDecl 0x[[#%x,SCOPED_MEMBER_ENUM_E3:]] <line:[[@LINE-22]]:5, col:21> col:16 class E3 'T' // CHECK-NEXT: | `-EnumDecl 0x[[#%x,SCOPED_MEMBER_ENUM_E4:]] <line:[[@LINE-22]]:5, col:21> col:16 class E4 'int' // CHECK-NEXT: |-ClassTemplateSpecialization 0x{{.+}} 'TestClassTemplateWithScopedMemberEnum' -// CHECK-NEXT: `-ClassTemplateSpecializationDecl 0x{{.+}} <line:[[@LINE-28]]:3, line:[[@LINE-23]]:3> line:[[@LINE-28]]:31 struct TestClassTemplateWithScopedMemberEnum definition implicit_instantiation +// CHECK-NEXT: `-ClassTemplateSpecializationDecl 0x{{.+}} <line:[[@LINE-28]]:24, line:[[@LINE-23]]:3> line:[[@LINE-28]]:31 struct TestClassTemplateWithScopedMemberEnum definition implicit_instantiation // CHECK: |-TemplateArgument type 'int' // CHECK-NEXT: | `-BuiltinType 0x{{.+}} 'int' // CHECK: |-EnumDecl 0x{{.+}} <line:[[@LINE-30]]:5, col:21> col:16 class E1 'int' instantiated_from 0x[[#SCOPED_MEMBER_ENUM_E1]]{{$}} @@ -635,7 +635,7 @@ namespace testCanonicalTemplate { // CHECK-NEXT: | `-ClassTemplateDecl 0x{{.+}} parent 0x{{.+}} <col:5, col:40> col:40 friend_undeclared TestClassTemplate{{$}} // CHECK-NEXT: | |-TemplateTypeParmDecl 0x{{.+}} <col:14, col:23> col:23 typename depth 1 index 0 T2{{$}} // CHECK-NEXT: | `-CXXRecordDecl 0x{{.+}} parent 0x{{.+}} <col:34, col:40> col:40 class TestClassTemplate{{$}} - // CHECK-NEXT: `-ClassTemplateSpecializationDecl 0x{{.+}} <line:[[@LINE-19]]:3, line:[[@LINE-17]]:3> line:[[@LINE-19]]:31 class TestClassTemplate definition implicit_instantiation{{$}} + // CHECK-NEXT: `-ClassTemplateSpecializationDecl 0x{{.+}} <line:[[@LINE-19]]:25, line:[[@LINE-17]]:3> line:[[@LINE-19]]:31 class TestClassTemplate definition implicit_instantiation{{$}} // CHECK-NEXT: |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init{{$}} // CHECK-NEXT: | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr{{$}} // CHECK-NEXT: | |-CopyConstructor simple trivial has_const_param implicit_has_const_param{{$}} @@ -668,7 +668,7 @@ namespace testCanonicalTemplate { // CHECK: ClassTemplateDecl 0x{{.+}} <{{.+}}:[[@LINE-5]]:3, col:31> col:31 TestClassTemplate2{{$}} // CHECK-NEXT: |-TemplateTypeParmDecl 0x{{.+}} <col:12, col:21> col:21 typename depth 0 index 0 T1{{$}} // CHECK-NEXT: |-CXXRecordDecl 0x{{.+}} <col:25, col:31> col:31 class TestClassTemplate2{{$}} - // CHECK-NEXT: `-ClassTemplateSpecializationDecl 0x{{.+}} <line:[[@LINE-6]]:3, line:[[@LINE-5]]:3> line:[[@LINE-6]]:31 class TestClassTemplate2 definition implicit_instantiation{{$}} + // CHECK-NEXT: `-ClassTemplateSpecializationDecl 0x{{.+}} <line:[[@LINE-6]]:25, line:[[@LINE-5]]:3> line:[[@LINE-6]]:31 class TestClassTemplate2 definition implicit_instantiation{{$}} // CHECK-NEXT: |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init{{$}} // CHECK-NEXT: | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr{{$}} // CHECK-NEXT: | |-CopyConstructor simple trivial has_const_param implicit_has_const_param{{$}} diff --git a/clang/test/AST/ast-dump-openmp-begin-declare-variant_template_3.cpp b/clang/test/AST/ast-dump-openmp-begin-declare-variant_template_3.cpp index 44d1cb462cd58..779dfec42ea8e 100644 --- a/clang/test/AST/ast-dump-openmp-begin-declare-variant_template_3.cpp +++ b/clang/test/AST/ast-dump-openmp-begin-declare-variant_template_3.cpp @@ -59,7 +59,7 @@ int test() { // CHECK-NEXT: | | |-ParmVarDecl [[ADDR_5:0x[a-z0-9]*]] <col:5> col:8 'int' // CHECK-NEXT: | | |-ParmVarDecl [[ADDR_6:0x[a-z0-9]*]] <col:10, col:12> col:13 'T *' // CHECK-NEXT: | | `-CompoundStmt [[ADDR_7:0x[a-z0-9]*]] <col:15, col:16> -// CHECK-NEXT: | |-ClassTemplateSpecializationDecl [[ADDR_8:0x[a-z0-9]*]] <line:5:1, line:7:1> line:5:30 struct S definition +// CHECK-NEXT: | |-ClassTemplateSpecializationDecl [[ADDR_8:0x[a-z0-9]*]] <line:5:23, line:7:1> line:5:30 struct S definition // CHECK-NEXT: | | |-DefinitionData pass_in_registers empty standard_layout trivially_copyable has_user_declared_ctor can_const_default_init // CHECK-NEXT: | | | |-DefaultConstructor defaulted_is_constexpr // CHECK-NEXT: | | | |-CopyConstructor simple trivial has_const_param implicit_has_const_param @@ -82,7 +82,7 @@ int test() { // CXX17-NEXT: | | |-CXXConstructorDecl [[ADDR_16:0x[a-z0-9]*]] <col:30> col:30 implicit constexpr S 'void (S<int> &&)' inline default trivial noexcept-unevaluated // CXX17-NEXT: | | | `-ParmVarDecl [[ADDR_17:0x[a-z0-9]*]] <col:30> col:30 'S<int> &&' // CHECK-NEXT: | | `-CXXDestructorDecl [[ADDR_19:0x[a-z0-9]*]] <col:30> col:30 implicit referenced {{(constexpr )?}}~S 'void ({{.*}}) noexcept' inline default trivial -// CHECK-NEXT: | `-ClassTemplateSpecializationDecl [[ADDR_20:0x[a-z0-9]*]] <col:1, line:7:1> line:5:30 struct S +// CHECK-NEXT: | `-ClassTemplateSpecializationDecl [[ADDR_20:0x[a-z0-9]*]] <col:23, line:7:1> line:5:30 struct S // CHECK-NEXT: | `-TemplateArgument type 'double' // CHECK-NEXT: | `-BuiltinType [[ADDR_21:0x[a-z0-9]*]] 'double' // CHECK-NEXT: |-FunctionTemplateDecl [[ADDR_22:0x[a-z0-9]*]] <line:9:1, line:12:1> line:10:5 also_before diff --git a/clang/test/AST/ast-dump-template-decls-json.cpp b/clang/test/AST/ast-dump-template-decls-json.cpp index 70f1d3b55f3ee..240469955f4b6 100644 --- a/clang/test/AST/ast-dump-template-decls-json.cpp +++ b/clang/test/AST/ast-dump-template-decls-json.cpp @@ -2872,8 +2872,8 @@ W(int)->W<1>; // CHECK-NEXT: "range": { // CHECK-NEXT: "begin": { // CHECK-NEXT: "offset": {{[0-9]+}}, -// CHECK-NEXT: "col": 1, -// CHECK-NEXT: "tokLen": 8 +// CHECK-NEXT: "col": 18, +// CHECK-NEXT: "tokLen": 5 // CHECK-NEXT: }, // CHECK-NEXT: "end": { // CHECK-NEXT: "offset": {{[0-9]+}}, diff --git a/clang/test/AST/ast-dump-template-redecl.cpp b/clang/test/AST/ast-dump-template-redecl.cpp new file mode 100644 index 0000000000000..a883375c1630b --- /dev/null +++ b/clang/test/AST/ast-dump-template-redecl.cpp @@ -0,0 +1,88 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fopenmp -verify -ast-dump %s %std_cxx17- | FileCheck %s --check-prefixes=CHECK +// expected-no-diagnostics + +template <class T> +struct Redeclared { + void function() {} +}; + +template <class T> +struct Redeclared; + +Redeclared<int> instantiation; + +template <typename T> +void redeclaredFunction(T t) { + (void)t; +} + +template <typename T> +void redeclaredFunction(T t); + +void instantiate() { + redeclaredFunction(0); +} + + +// CHECK: |-ClassTemplateDecl [[ADDR_0:0x[a-z0-9]*]] <{{.*}}:4:1, line:7:1> line:5:8 Redeclared +// CHECK-NEXT: | |-TemplateTypeParmDecl [[ADDR_1:0x[a-z0-9]*]] <line:4:11, col:17> col:17 class depth 0 index 0 T +// CHECK-NEXT: | |-CXXRecordDecl [[ADDR_2:0x[a-z0-9]*]] <line:5:1, line:7:1> line:5:8 struct Redeclared definition +// CHECK-NEXT: | | |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init +// CHECK-NEXT: | | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr +// CHECK-NEXT: | | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param +// CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit +// CHECK-NEXT: | | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param +// CHECK-NEXT: | | | |-MoveAssignment exists simple trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial {{(constexpr )?}}needs_implicit +// CHECK-NEXT: | | |-CXXRecordDecl [[ADDR_3:0x[a-z0-9]*]] <col:1, col:8> col:8 implicit struct Redeclared +// CHECK-NEXT: | | `-CXXMethodDecl [[ADDR_4:0x[a-z0-9]*]] <line:6:3, col:20> col:8 function 'void ()' implicit-inline +// CHECK-NEXT: | | `-CompoundStmt [[ADDR_5:0x[a-z0-9]*]] <col:19, col:20> +// CHECK-NEXT: | `-ClassTemplateSpecializationDecl [[ADDR_6:0x[a-z0-9]*]] <line:5:1, line:7:1> line:5:8 struct Redeclared definition implicit_instantiation +// CHECK-NEXT: | |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init +// CHECK-NEXT: | | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr +// CHECK-NEXT: | | |-CopyConstructor simple trivial has_const_param implicit_has_const_param +// CHECK-NEXT: | | |-MoveConstructor exists simple trivial +// CHECK-NEXT: | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param +// CHECK-NEXT: | | |-MoveAssignment exists simple trivial needs_implicit +// CHECK-NEXT: | | `-Destructor simple irrelevant trivial constexpr needs_implicit +// CHECK-NEXT: | |-TemplateArgument type 'int' +// CHECK-NEXT: | | `-BuiltinType [[ADDR_7:0x[a-z0-9]*]] 'int' +// CHECK-NEXT: | |-CXXRecordDecl [[ADDR_8:0x[a-z0-9]*]] <col:1, col:8> col:8 implicit struct Redeclared +// CHECK-NEXT: | |-CXXMethodDecl [[ADDR_9:0x[a-z0-9]*]] <line:6:3, col:20> col:8 function 'void ()' implicit_instantiation implicit-inline instantiated_from [[ADDR_4]] +// CHECK-NEXT: | |-CXXConstructorDecl [[ADDR_10:0x[a-z0-9]*]] <line:5:8> col:8 implicit used constexpr Redeclared 'void () noexcept' inline default trivial +// CHECK-NEXT: | | `-CompoundStmt [[ADDR_11:0x[a-z0-9]*]] <col:8> +// CHECK-NEXT: | |-CXXConstructorDecl [[ADDR_12:0x[a-z0-9]*]] <col:8> col:8 implicit constexpr Redeclared 'void (const Redeclared<int> &)' inline default trivial noexcept-unevaluated [[ADDR_12]] +// CHECK-NEXT: | | `-ParmVarDecl [[ADDR_13:0x[a-z0-9]*]] <col:8> col:8 'const Redeclared<int> &' +// CHECK-NEXT: | `-CXXConstructorDecl [[ADDR_14:0x[a-z0-9]*]] <col:8> col:8 implicit constexpr Redeclared 'void (Redeclared<int> &&)' inline default trivial noexcept-unevaluated [[ADDR_14]] +// CHECK-NEXT: | `-ParmVarDecl [[ADDR_15:0x[a-z0-9]*]] <col:8> col:8 'Redeclared<int> &&' +// CHECK-NEXT: |-ClassTemplateDecl [[ADDR_16:0x[a-z0-9]*]] prev [[ADDR_0]] <line:9:1, line:10:8> col:8 Redeclared +// CHECK-NEXT: | |-TemplateTypeParmDecl [[ADDR_17:0x[a-z0-9]*]] <line:9:11, col:17> col:17 class depth 0 index 0 T +// CHECK-NEXT: | |-CXXRecordDecl [[ADDR_18:0x[a-z0-9]*]] prev [[ADDR_2]] <line:10:1, col:8> col:8 struct Redeclared +// CHECK-NEXT: | `-ClassTemplateSpecialization [[ADDR_19:0x[a-z0-9]*]] 'Redeclared' +// CHECK-NEXT: |-VarDecl [[ADDR_20:0x[a-z0-9]*]] <line:12:1, col:17> col:17 instantiation 'Redeclared<int>' callinit +// CHECK-NEXT: | `-CXXConstructExpr [[ADDR_21:0x[a-z0-9]*]] <col:17> 'Redeclared<int>' 'void () noexcept' +// CHECK-NEXT: |-FunctionTemplateDecl [[ADDR_22:0x[a-z0-9]*]] <line:14:1, line:17:1> line:15:6 redeclaredFunction +// CHECK-NEXT: | |-TemplateTypeParmDecl [[ADDR_23:0x[a-z0-9]*]] <line:14:11, col:20> col:20 referenced typename depth 0 index 0 T +// CHECK-NEXT: | |-FunctionDecl [[ADDR_24:0x[a-z0-9]*]] <line:15:1, line:17:1> line:15:6 redeclaredFunction 'void (T)' +// CHECK-NEXT: | | |-ParmVarDecl [[ADDR_25:0x[a-z0-9]*]] <col:25, col:27> col:27 referenced t 'T' +// CHECK-NEXT: | | `-CompoundStmt [[ADDR_26:0x[a-z0-9]*]] <col:30, line:17:1> +// CHECK-NEXT: | | `-CStyleCastExpr [[ADDR_27:0x[a-z0-9]*]] <line:16:3, col:9> 'void' <ToVoid> +// CHECK-NEXT: | | `-DeclRefExpr [[ADDR_28:0x[a-z0-9]*]] <col:9> 'T' lvalue ParmVar [[ADDR_25]] 't' 'T' +// CHECK-NEXT: | `-FunctionDecl [[ADDR_29:0x[a-z0-9]*]] <line:15:1, line:17:1> line:15:6 used redeclaredFunction 'void (int)' implicit_instantiation +// CHECK-NEXT: | |-TemplateArgument type 'int' +// CHECK-NEXT: | | `-BuiltinType [[ADDR_30:0x[a-z0-9]*]] 'int' +// CHECK-NEXT: | |-ParmVarDecl [[ADDR_31:0x[a-z0-9]*]] <line:20:25, col:27> col:27 used t 'int' +// CHECK-NEXT: | `-CompoundStmt [[ADDR_32:0x[a-z0-9]*]] <line:15:30, line:17:1> +// CHECK-NEXT: | `-CStyleCastExpr [[ADDR_33:0x[a-z0-9]*]] <line:16:3, col:9> 'void' <ToVoid> +// CHECK-NEXT: | `-DeclRefExpr [[ADDR_34:0x[a-z0-9]*]] <col:9> 'int' lvalue ParmVar [[ADDR_31]] 't' 'int' +// CHECK-NEXT: |-FunctionTemplateDecl [[ADDR_35:0x[a-z0-9]*]] prev [[ADDR_22]] <line:19:1, line:20:28> col:6 redeclaredFunction +// CHECK-NEXT: | |-TemplateTypeParmDecl [[ADDR_36:0x[a-z0-9]*]] <line:19:11, col:20> col:20 referenced typename depth 0 index 0 T +// CHECK-NEXT: | |-FunctionDecl [[ADDR_37:0x[a-z0-9]*]] prev [[ADDR_24]] <line:20:1, col:28> col:6 redeclaredFunction 'void (T)' +// CHECK-NEXT: | | `-ParmVarDecl [[ADDR_38:0x[a-z0-9]*]] <col:25, col:27> col:27 t 'T' +// CHECK-NEXT: | `-Function [[ADDR_39:0x[a-z0-9]*]] 'redeclaredFunction' 'void (int)' +// CHECK-NEXT: `-FunctionDecl [[ADDR_40:0x[a-z0-9]*]] <line:22:1, line:24:1> line:22:6 instantiate 'void ()' +// CHECK-NEXT: `-CompoundStmt [[ADDR_41:0x[a-z0-9]*]] <col:20, line:24:1> +// CHECK-NEXT: `-CallExpr [[ADDR_42:0x[a-z0-9]*]] <line:23:3, col:23> 'void' +// CHECK-NEXT: |-ImplicitCastExpr [[ADDR_43:0x[a-z0-9]*]] <col:3> 'void (*)(int)' <FunctionToPointerDecay> +// CHECK-NEXT: | `-DeclRefExpr [[ADDR_44:0x[a-z0-9]*]] <col:3> 'void (int)' lvalue Function [[ADDR_29]] 'redeclaredFunction' 'void (int)' (FunctionTemplate [[ADDR_35]] 'redeclaredFunction') +// CHECK-NEXT: `-IntegerLiteral [[ADDR_45:0x[a-z0-9]*]] <col:22> 'int' 0 diff --git a/clang/test/AST/ast-dump-templates.cpp b/clang/test/AST/ast-dump-templates.cpp index e43fe6b1dda25..7ac1ae5e2db1c 100644 --- a/clang/test/AST/ast-dump-templates.cpp +++ b/clang/test/AST/ast-dump-templates.cpp @@ -889,10 +889,9 @@ namespace GH153540 { // JSON-NEXT: }, // JSON-NEXT: "range": { // JSON-NEXT: "begin": { -// JSON-NEXT: "offset": 765, -// JSON-NEXT: "line": 14, +// JSON-NEXT: "offset": 805, // JSON-NEXT: "col": 1, -// JSON-NEXT: "tokLen": 8 +// JSON-NEXT: "tokLen": 6 // JSON-NEXT: }, // JSON-NEXT: "end": { // JSON-NEXT: "offset": 879, @@ -1526,10 +1525,9 @@ namespace GH153540 { // JSON-NEXT: }, // JSON-NEXT: "range": { // JSON-NEXT: "begin": { -// JSON-NEXT: "offset": 765, -// JSON-NEXT: "line": 14, +// JSON-NEXT: "offset": 805, // JSON-NEXT: "col": 1, -// JSON-NEXT: "tokLen": 8 +// JSON-NEXT: "tokLen": 6 // JSON-NEXT: }, // JSON-NEXT: "end": { // JSON-NEXT: "offset": 879, @@ -4022,9 +4020,9 @@ namespace GH153540 { // JSON-NEXT: }, // JSON-NEXT: "range": { // JSON-NEXT: "begin": { -// JSON-NEXT: "offset": 2263, -// JSON-NEXT: "col": 3, -// JSON-NEXT: "tokLen": 8 +// JSON-NEXT: "offset": 2284, +// JSON-NEXT: "col": 24, +// JSON-NEXT: "tokLen": 6 // JSON-NEXT: }, // JSON-NEXT: "end": { // JSON-NEXT: "offset": 2294, @@ -4581,10 +4579,9 @@ namespace GH153540 { // JSON-NEXT: }, // JSON-NEXT: "range": { // JSON-NEXT: "begin": { -// JSON-NEXT: "offset": 2408, -// JSON-NEXT: "line": 80, +// JSON-NEXT: "offset": 2438, // JSON-NEXT: "col": 1, -// JSON-NEXT: "tokLen": 8 +// JSON-NEXT: "tokLen": 6 // JSON-NEXT: }, // JSON-NEXT: "end": { // JSON-NEXT: "offset": 2471, @@ -8213,9 +8210,9 @@ namespace GH153540 { // JSON-NEXT: }, // JSON-NEXT: "range": { // JSON-NEXT: "begin": { -// JSON-NEXT: "offset": 9464, -// JSON-NEXT: "col": 5, -// JSON-NEXT: "tokLen": 8 +// JSON-NEXT: "offset": 9485, +// JSON-NEXT: "col": 26, +// JSON-NEXT: "tokLen": 6 // JSON-NEXT: }, // JSON-NEXT: "end": { // JSON-NEXT: "offset": 9502, diff --git a/clang/test/ParserHLSL/hlsl_resource_class_attr.hlsl b/clang/test/ParserHLSL/hlsl_resource_class_attr.hlsl index fbada8b4b99f7..6ceaf8fae102e 100644 --- a/clang/test/ParserHLSL/hlsl_resource_class_attr.hlsl +++ b/clang/test/ParserHLSL/hlsl_resource_class_attr.hlsl @@ -28,7 +28,7 @@ template<typename T> struct MyBuffer2 { __hlsl_resource_t [[hlsl::resource_class(UAV)]] h; }; -// CHECK: ClassTemplateSpecializationDecl 0x{{[0-9a-f]+}} <line:[[# @LINE - 4]]:1, line:[[# @LINE - 2]]:1> line:[[# @LINE - 4]]:29 struct MyBuffer2 definition implicit_instantiation +// CHECK: ClassTemplateSpecializationDecl 0x{{[0-9a-f]+}} <line:[[# @LINE - 4]]:22, line:[[# @LINE - 2]]:1> line:[[# @LINE - 4]]:29 struct MyBuffer2 definition implicit_instantiation // CHECK: TemplateArgument type 'float' // CHECK: BuiltinType 0x{{[0-9a-f]+}} 'float' // CHECK: CXXRecordDecl 0x{{[0-9a-f]+}} <col:22, col:29> col:29 implicit struct MyBuffer2 >From 69eb60ca9836ed603b26bf0278bf3b664cb1adb0 Mon Sep 17 00:00:00 2001 From: Fred Tingaud <[email protected]> Date: Mon, 29 Sep 2025 18:37:48 +0200 Subject: [PATCH 2/2] CFG: Add a BuildOption to consider default branch of switch on covered enumerations. By default, the `default:` branch of a switch on fully covered enumarations is considered as "Unreachable". It is a sane assumption in most cases, but not always. That commit allows to change such behavior when needed. --- clang/include/clang/Analysis/CFG.h | 1 + clang/lib/Analysis/CFG.cpp | 5 +- clang/unittests/Analysis/CFGTest.cpp | 153 +++++++++++++++++++++++++++ 3 files changed, 158 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Analysis/CFG.h b/clang/include/clang/Analysis/CFG.h index 1b1ff5e558ec5..edfd655b579b9 100644 --- a/clang/include/clang/Analysis/CFG.h +++ b/clang/include/clang/Analysis/CFG.h @@ -1251,6 +1251,7 @@ class CFG { bool MarkElidedCXXConstructors = false; bool AddVirtualBaseBranches = false; bool OmitImplicitValueInitializers = false; + bool SwitchKeepDefaultCoveredEnum = false; BuildOptions() = default; diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index d960d5130332b..c7cccf7cd291b 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -4515,9 +4515,12 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) { // // Note: We add a successor to a switch that is considered covered yet has no // case statements if the enumeration has no enumerators. + // We also add a successor if BuildOpts.SwitchReqDefaultCoveredEnum is + // true bool SwitchAlwaysHasSuccessor = false; SwitchAlwaysHasSuccessor |= switchExclusivelyCovered; - SwitchAlwaysHasSuccessor |= Terminator->isAllEnumCasesCovered() && + SwitchAlwaysHasSuccessor |= !BuildOpts.SwitchKeepDefaultCoveredEnum && + Terminator->isAllEnumCasesCovered() && Terminator->getSwitchCaseList(); addSuccessor(SwitchTerminatedBlock, DefaultCaseBlock, !SwitchAlwaysHasSuccessor); diff --git a/clang/unittests/Analysis/CFGTest.cpp b/clang/unittests/Analysis/CFGTest.cpp index 46a6751391cf5..186724b103ce9 100644 --- a/clang/unittests/Analysis/CFGTest.cpp +++ b/clang/unittests/Analysis/CFGTest.cpp @@ -93,6 +93,159 @@ TEST(CFG, DependantBaseAddImplicitDtors) { .getStatus()); } +TEST(CFG, SwitchCoveredEnumNoDefault) { + const char *Code = R"( + enum class E {E1, E2}; + int f(E e) { + switch(e) { + case E::E1: + return 1; + case E::E2: + return 2; + } + return 0; + } + )"; + CFG::BuildOptions Options; + Options.SwitchKeepDefaultCoveredEnum = true; + BuildResult B = BuildCFG(Code, Options); + EXPECT_EQ(BuildResult::BuiltCFG, B.getStatus()); + + // [B5 (ENTRY)] + // Succs (1): B2 + // + // [B1] + // 1: 0 + // 2: return [B1.1]; + // Preds (1): B2 + // Succs (1): B0 + // + // [B2] + // 1: e (ImplicitCastExpr, LValueToRValue, E) + // T: switch [B2.1] + // Preds (1): B5 + // Succs (3): B3 B4 B1 + // + // [B3] + // case E::E2: + // 1: 2 + // 2: return [B3.1]; + // Preds (1): B2 + // Succs (1): B0 + // + // [B4] + // case E::E1: + // 1: 1 + // 2: return [B4.1]; + // Preds (1): B2 + // Succs (1): B0 + // + // [B0 (EXIT)] + // Preds (3): B1 B3 B4 + + const auto &Entry = B.getCFG()->getEntry(); + EXPECT_EQ(1u, Entry.succ_size()); + // First successor of Entry is the switch + CFGBlock *SwitchBlock = *Entry.succ_begin(); + EXPECT_EQ(3u, SwitchBlock->succ_size()); + // Last successor of the switch is after the switch + auto NoCaseSucc = SwitchBlock->succ_rbegin(); + EXPECT_TRUE(NoCaseSucc->isReachable()); + + // Checking that the same node is Unreachable without this setting + Options.SwitchKeepDefaultCoveredEnum = false; + B = BuildCFG(Code, Options); + EXPECT_EQ(BuildResult::BuiltCFG, B.getStatus()); + + const auto &Entry2 = B.getCFG()->getEntry(); + EXPECT_EQ(1u, Entry2.succ_size()); + CFGBlock *SwitchBlock2 = *Entry2.succ_begin(); + EXPECT_EQ(3u, SwitchBlock2->succ_size()); + auto NoCaseSucc2 = SwitchBlock2->succ_rbegin(); + EXPECT_FALSE(NoCaseSucc2->isReachable()); +} + +TEST(CFG, SwitchCoveredEnumWithDefault) { + const char *Code = R"( + enum class E {E1, E2}; + int f(E e) { + switch(e) { + case E::E1: + return 1; + case E::E2: + return 2; + default: + return 0; + } + return -1; + } + )"; + CFG::BuildOptions Options; + Options.SwitchKeepDefaultCoveredEnum = true; + BuildResult B = BuildCFG(Code, Options); + EXPECT_EQ(BuildResult::BuiltCFG, B.getStatus()); + + // [B6 (ENTRY)] + // Succs (1): B2 + // + // [B1] + // 1: -1 + // 2: return [B1.1]; + // Succs (1): B0 + // + // [B2] + // 1: e (ImplicitCastExpr, LValueToRValue, E) + // T: switch [B2.1] + // Preds (1): B6 + // Succs (3): B4 B5 B3 + // + // [B3] + // default: + // 1: 0 + // 2: return [B3.1]; + // Preds (1): B2 + // Succs (1): B0 + // + // [B4] + // case E::E2: + // 1: 2 + // 2: return [B4.1]; + // Preds (1): B2 + // Succs (1): B0 + // + // [B5] + // case E::E1: + // 1: 1 + // 2: return [B5.1]; + // Preds (1): B2 + // Succs (1): B0 + // + // [B0 (EXIT)] + // Preds (4): B1 B3 B4 B5 + B.getCFG()->dump(B.getAST()->getLangOpts(), true); + + const auto &Entry = B.getCFG()->getEntry(); + EXPECT_EQ(1u, Entry.succ_size()); + // First successor of Entry is the switch + CFGBlock *SwitchBlock = *Entry.succ_begin(); + EXPECT_EQ(3u, SwitchBlock->succ_size()); + // Last successor of the switch is the default branch + auto defaultSucc = SwitchBlock->succ_rbegin(); + EXPECT_TRUE(defaultSucc->isReachable()); + + // Checking that the same node is Unreachable without this setting + Options.SwitchKeepDefaultCoveredEnum = false; + B = BuildCFG(Code, Options); + EXPECT_EQ(BuildResult::BuiltCFG, B.getStatus()); + + const auto &Entry2 = B.getCFG()->getEntry(); + EXPECT_EQ(1u, Entry2.succ_size()); + CFGBlock *SwitchBlock2 = *Entry2.succ_begin(); + EXPECT_EQ(3u, SwitchBlock2->succ_size()); + auto defaultSucc2 = SwitchBlock2->succ_rbegin(); + EXPECT_FALSE(defaultSucc2->isReachable()); +} + TEST(CFG, IsLinear) { auto expectLinear = [](bool IsLinear, const char *Code) { BuildResult B = BuildCFG(Code); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
