https://github.com/tcottin updated https://github.com/llvm/llvm-project/pull/174723
>From 6ea9e712b6948106c219ff44778cc51902b783ea Mon Sep 17 00:00:00 2001 From: Tim Cottin <[email protected]> Date: Wed, 7 Jan 2026 09:10:53 +0000 Subject: [PATCH 1/6] [clang] Add a valid begin source location for abbreviated function templates --- .../clangd/unittests/HoverTests.cpp | 61 ++++ clang/include/clang/AST/DeclTemplate.h | 13 + .../ast-dump-record-definition-data-json.cpp | 18 +- clang/test/AST/ast-dump-templates.cpp | 295 ++++++++++++++++++ 4 files changed, 384 insertions(+), 3 deletions(-) diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp index eb858ff616e90..1d1da620857fe 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -57,6 +57,67 @@ TEST(Hover, Structured) { HI.Type = "void ()"; HI.Parameters.emplace(); }}, + {R"cpp( + // Best foo ever. + void [[fo^o]](auto x) {} + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "foo"; + HI.Kind = index::SymbolKind::Function; + HI.Documentation = "Best foo ever."; + HI.Definition = "void foo(auto x)"; + HI.ReturnType = "void"; + HI.Type = "void (auto)"; + HI.TemplateParameters = { + {{"class"}, std::string("x:auto"), std::nullopt}, + }; + HI.Parameters = { + {{"auto"}, std::string("x"), std::nullopt}, + }; + }}, + {R"cpp( + // Best foo ever. + template <class T> + void [[fo^o]](T x) {} + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "foo"; + HI.Kind = index::SymbolKind::Function; + HI.Documentation = "Best foo ever."; + HI.Definition = "template <class T> void foo(T x)"; + HI.ReturnType = "void"; + HI.Type = "void (T)"; + HI.TemplateParameters = { + {{"class"}, std::string("T"), std::nullopt}, + }; + HI.Parameters = { + {{"T"}, std::string("x"), std::nullopt}, + }; + }}, + {R"cpp( + // Best foo ever. + template <class T> + void [[fo^o]](T x, auto y) {} + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "foo"; + HI.Kind = index::SymbolKind::Function; + HI.Documentation = "Best foo ever."; + HI.Definition = "template <class T> void foo(T x, auto y)"; + HI.ReturnType = "void"; + HI.Type = "void (T, auto)"; + HI.TemplateParameters = { + {{"class"}, std::string("T"), std::nullopt}, + {{"class"}, std::string("y:auto"), std::nullopt}, + }; + HI.Parameters = { + {{"T"}, std::string("x"), std::nullopt}, + {{"auto"}, std::string("y"), std::nullopt}, + }; + }}, // Inside namespace {R"cpp( namespace ns1 { namespace ns2 { diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index a4a1bb9c13c79..6a29265cfb642 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -1104,6 +1104,19 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl { static FunctionTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); + SourceRange getSourceRange() const override LLVM_READONLY { + SourceLocation BeginLoc = getTemplateParameters()->getTemplateLoc(); + if (BeginLoc.isInvalid() && isAbbreviated()) { + // The BeginLoc of FunctionTemplateDecls is derived from the template keyword. + // But "pure" abbreviated templates do not use the template keyword. + // Hence the BeginLoc is invalid. + // Therefore just use the beginning of the templated declaration instead. + BeginLoc = getTemplatedDecl()->getBeginLoc(); + } + + return SourceRange(BeginLoc, TemplatedDecl->getSourceRange().getEnd()); + } + // Implement isa/cast/dyncast support static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == FunctionTemplate; } diff --git a/clang/test/AST/ast-dump-record-definition-data-json.cpp b/clang/test/AST/ast-dump-record-definition-data-json.cpp index e35bec78c6847..62acf84e6757c 100644 --- a/clang/test/AST/ast-dump-record-definition-data-json.cpp +++ b/clang/test/AST/ast-dump-record-definition-data-json.cpp @@ -408,7 +408,11 @@ struct DoesNotAllowConstDefaultInit { // CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": {}, +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 197, +// CHECK-NEXT: "col": 33, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, // CHECK-NEXT: "end": { // CHECK-NEXT: "offset": 199, // CHECK-NEXT: "col": 35, @@ -523,7 +527,11 @@ struct DoesNotAllowConstDefaultInit { // CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": {}, +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 190, +// CHECK-NEXT: "col": 26, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, // CHECK-NEXT: "end": { // CHECK-NEXT: "offset": 199, // CHECK-NEXT: "col": 35, @@ -598,7 +606,11 @@ struct DoesNotAllowConstDefaultInit { // CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": {}, +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 190, +// CHECK-NEXT: "col": 26, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, // CHECK-NEXT: "end": { // CHECK-NEXT: "offset": 199, // CHECK-NEXT: "col": 35, diff --git a/clang/test/AST/ast-dump-templates.cpp b/clang/test/AST/ast-dump-templates.cpp index f0357c5a8aa32..8cf9b6a29e332 100644 --- a/clang/test/AST/ast-dump-templates.cpp +++ b/clang/test/AST/ast-dump-templates.cpp @@ -266,6 +266,17 @@ namespace AliasDependentTemplateSpecializationType { // DUMP-NEXT: `-BuiltinType {{.*}} 'int' } // namespace +namespace TestAbbreviatedTemplateDecls { + // DUMP-LABEL: NamespaceDecl {{.*}} TestAbbreviatedTemplateDecls{{$}} + void abbreviated(auto); + template<class T> + void mixed(T, auto); + +// DUMP: FunctionTemplateDecl {{.*}} <line:[[@LINE-4]]:3, col:24> col:8 abbreviated +// DUMP: FunctionTemplateDecl {{.*}} <line:[[@LINE-4]]:3, line:[[@LINE-3]]:21> col:8 mixed + +} // namespace TestAbbreviatedTemplateDecls + // NOTE: CHECK lines have been autogenerated by gen_ast_dump_json_test.py @@ -9256,6 +9267,290 @@ namespace AliasDependentTemplateSpecializationType { // JSON-NEXT: ] // JSON-NEXT: } // JSON-NEXT: ] +// JSON-NEXT: }, +// JSON-NEXT: { +// JSON-NEXT: "id": "0x{{.*}}", +// JSON-NEXT: "kind": "NamespaceDecl", +// JSON-NEXT: "loc": { +// JSON-NEXT: "offset": 11557, +// JSON-NEXT: "line": 269, +// JSON-NEXT: "col": 11, +// JSON-NEXT: "tokLen": 28 +// JSON-NEXT: }, +// JSON-NEXT: "range": { +// JSON-NEXT: "begin": { +// JSON-NEXT: "offset": 11547, +// JSON-NEXT: "col": 1, +// JSON-NEXT: "tokLen": 9 +// JSON-NEXT: }, +// JSON-NEXT: "end": { +// JSON-NEXT: "offset": 11906, +// JSON-NEXT: "line": 278, +// JSON-NEXT: "col": 1, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: } +// JSON-NEXT: }, +// JSON-NEXT: "name": "TestAbbreviatedTemplateDecls", +// JSON-NEXT: "inner": [ +// JSON-NEXT: { +// JSON-NEXT: "id": "0x{{.*}}", +// JSON-NEXT: "kind": "FunctionTemplateDecl", +// JSON-NEXT: "loc": { +// JSON-NEXT: "offset": 11667, +// JSON-NEXT: "line": 271, +// JSON-NEXT: "col": 8, +// JSON-NEXT: "tokLen": 11 +// JSON-NEXT: }, +// JSON-NEXT: "range": { +// JSON-NEXT: "begin": { +// JSON-NEXT: "offset": 11662, +// JSON-NEXT: "col": 3, +// JSON-NEXT: "tokLen": 4 +// JSON-NEXT: }, +// JSON-NEXT: "end": { +// JSON-NEXT: "offset": 11683, +// JSON-NEXT: "col": 24, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: } +// JSON-NEXT: }, +// JSON-NEXT: "name": "abbreviated", +// JSON-NEXT: "inner": [ +// JSON-NEXT: { +// JSON-NEXT: "id": "0x{{.*}}", +// JSON-NEXT: "kind": "TemplateTypeParmDecl", +// JSON-NEXT: "loc": { +// JSON-NEXT: "offset": 11683, +// JSON-NEXT: "col": 24, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: }, +// JSON-NEXT: "range": { +// JSON-NEXT: "begin": { +// JSON-NEXT: "offset": 11679, +// JSON-NEXT: "col": 20, +// JSON-NEXT: "tokLen": 4 +// JSON-NEXT: }, +// JSON-NEXT: "end": { +// JSON-NEXT: "offset": 11683, +// JSON-NEXT: "col": 24, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: } +// JSON-NEXT: }, +// JSON-NEXT: "isImplicit": true, +// JSON-NEXT: "name": "auto:1", +// JSON-NEXT: "tagUsed": "class", +// JSON-NEXT: "depth": 0, +// JSON-NEXT: "index": 0 +// JSON-NEXT: }, +// JSON-NEXT: { +// JSON-NEXT: "id": "0x{{.*}}", +// JSON-NEXT: "kind": "FunctionDecl", +// JSON-NEXT: "loc": { +// JSON-NEXT: "offset": 11667, +// JSON-NEXT: "col": 8, +// JSON-NEXT: "tokLen": 11 +// JSON-NEXT: }, +// JSON-NEXT: "range": { +// JSON-NEXT: "begin": { +// JSON-NEXT: "offset": 11662, +// JSON-NEXT: "col": 3, +// JSON-NEXT: "tokLen": 4 +// JSON-NEXT: }, +// JSON-NEXT: "end": { +// JSON-NEXT: "offset": 11683, +// JSON-NEXT: "col": 24, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: } +// JSON-NEXT: }, +// JSON-NEXT: "name": "abbreviated", +// JSON-NEXT: "type": { +// JSON-NEXT: "qualType": "void (auto)" +// JSON-NEXT: }, +// JSON-NEXT: "inner": [ +// JSON-NEXT: { +// JSON-NEXT: "id": "0x{{.*}}", +// JSON-NEXT: "kind": "ParmVarDecl", +// JSON-NEXT: "loc": { +// JSON-NEXT: "offset": 11683, +// JSON-NEXT: "col": 24, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: }, +// JSON-NEXT: "range": { +// JSON-NEXT: "begin": { +// JSON-NEXT: "offset": 11679, +// JSON-NEXT: "col": 20, +// JSON-NEXT: "tokLen": 4 +// JSON-NEXT: }, +// JSON-NEXT: "end": { +// JSON-NEXT: "offset": 11679, +// JSON-NEXT: "col": 20, +// JSON-NEXT: "tokLen": 4 +// JSON-NEXT: } +// JSON-NEXT: }, +// JSON-NEXT: "type": { +// JSON-NEXT: "qualType": "auto" +// JSON-NEXT: } +// JSON-NEXT: } +// JSON-NEXT: ] +// JSON-NEXT: } +// JSON-NEXT: ] +// JSON-NEXT: }, +// JSON-NEXT: { +// JSON-NEXT: "id": "0x{{.*}}", +// JSON-NEXT: "kind": "FunctionTemplateDecl", +// JSON-NEXT: "loc": { +// JSON-NEXT: "offset": 11713, +// JSON-NEXT: "line": 273, +// JSON-NEXT: "col": 8, +// JSON-NEXT: "tokLen": 5 +// JSON-NEXT: }, +// JSON-NEXT: "range": { +// JSON-NEXT: "begin": { +// JSON-NEXT: "offset": 11688, +// JSON-NEXT: "line": 272, +// JSON-NEXT: "col": 3, +// JSON-NEXT: "tokLen": 8 +// JSON-NEXT: }, +// JSON-NEXT: "end": { +// JSON-NEXT: "offset": 11726, +// JSON-NEXT: "line": 273, +// JSON-NEXT: "col": 21, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: } +// JSON-NEXT: }, +// JSON-NEXT: "name": "mixed", +// JSON-NEXT: "inner": [ +// JSON-NEXT: { +// JSON-NEXT: "id": "0x{{.*}}", +// JSON-NEXT: "kind": "TemplateTypeParmDecl", +// JSON-NEXT: "loc": { +// JSON-NEXT: "offset": 11703, +// JSON-NEXT: "line": 272, +// JSON-NEXT: "col": 18, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: }, +// JSON-NEXT: "range": { +// JSON-NEXT: "begin": { +// JSON-NEXT: "offset": 11697, +// JSON-NEXT: "col": 12, +// JSON-NEXT: "tokLen": 5 +// JSON-NEXT: }, +// JSON-NEXT: "end": { +// JSON-NEXT: "offset": 11703, +// JSON-NEXT: "col": 18, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: } +// JSON-NEXT: }, +// JSON-NEXT: "isReferenced": true, +// JSON-NEXT: "name": "T", +// JSON-NEXT: "tagUsed": "class", +// JSON-NEXT: "depth": 0, +// JSON-NEXT: "index": 0 +// JSON-NEXT: }, +// JSON-NEXT: { +// JSON-NEXT: "id": "0x{{.*}}", +// JSON-NEXT: "kind": "TemplateTypeParmDecl", +// JSON-NEXT: "loc": { +// JSON-NEXT: "offset": 11726, +// JSON-NEXT: "line": 273, +// JSON-NEXT: "col": 21, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: }, +// JSON-NEXT: "range": { +// JSON-NEXT: "begin": { +// JSON-NEXT: "offset": 11722, +// JSON-NEXT: "col": 17, +// JSON-NEXT: "tokLen": 4 +// JSON-NEXT: }, +// JSON-NEXT: "end": { +// JSON-NEXT: "offset": 11726, +// JSON-NEXT: "col": 21, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: } +// JSON-NEXT: }, +// JSON-NEXT: "isImplicit": true, +// JSON-NEXT: "name": "auto:2", +// JSON-NEXT: "tagUsed": "class", +// JSON-NEXT: "depth": 0, +// JSON-NEXT: "index": 1 +// JSON-NEXT: }, +// JSON-NEXT: { +// JSON-NEXT: "id": "0x{{.*}}", +// JSON-NEXT: "kind": "FunctionDecl", +// JSON-NEXT: "loc": { +// JSON-NEXT: "offset": 11713, +// JSON-NEXT: "col": 8, +// JSON-NEXT: "tokLen": 5 +// JSON-NEXT: }, +// JSON-NEXT: "range": { +// JSON-NEXT: "begin": { +// JSON-NEXT: "offset": 11708, +// JSON-NEXT: "col": 3, +// JSON-NEXT: "tokLen": 4 +// JSON-NEXT: }, +// JSON-NEXT: "end": { +// JSON-NEXT: "offset": 11726, +// JSON-NEXT: "col": 21, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: } +// JSON-NEXT: }, +// JSON-NEXT: "name": "mixed", +// JSON-NEXT: "type": { +// JSON-NEXT: "qualType": "void (T, auto)" +// JSON-NEXT: }, +// JSON-NEXT: "inner": [ +// JSON-NEXT: { +// JSON-NEXT: "id": "0x{{.*}}", +// JSON-NEXT: "kind": "ParmVarDecl", +// JSON-NEXT: "loc": { +// JSON-NEXT: "offset": 11720, +// JSON-NEXT: "col": 15, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: }, +// JSON-NEXT: "range": { +// JSON-NEXT: "begin": { +// JSON-NEXT: "offset": 11719, +// JSON-NEXT: "col": 14, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: }, +// JSON-NEXT: "end": { +// JSON-NEXT: "offset": 11719, +// JSON-NEXT: "col": 14, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: } +// JSON-NEXT: }, +// JSON-NEXT: "type": { +// JSON-NEXT: "qualType": "T" +// JSON-NEXT: } +// JSON-NEXT: }, +// JSON-NEXT: { +// JSON-NEXT: "id": "0x{{.*}}", +// JSON-NEXT: "kind": "ParmVarDecl", +// JSON-NEXT: "loc": { +// JSON-NEXT: "offset": 11726, +// JSON-NEXT: "col": 21, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: }, +// JSON-NEXT: "range": { +// JSON-NEXT: "begin": { +// JSON-NEXT: "offset": 11722, +// JSON-NEXT: "col": 17, +// JSON-NEXT: "tokLen": 4 +// JSON-NEXT: }, +// JSON-NEXT: "end": { +// JSON-NEXT: "offset": 11722, +// JSON-NEXT: "col": 17, +// JSON-NEXT: "tokLen": 4 +// JSON-NEXT: } +// JSON-NEXT: }, +// JSON-NEXT: "type": { +// JSON-NEXT: "qualType": "auto" +// JSON-NEXT: } +// JSON-NEXT: } +// JSON-NEXT: ] +// JSON-NEXT: } +// JSON-NEXT: ] +// JSON-NEXT: } +// JSON-NEXT: ] // JSON-NEXT: } // JSON-NEXT: ] // JSON-NEXT: } >From 1dd047d4c0f72e2448ba5a3b34900c1b85f49728 Mon Sep 17 00:00:00 2001 From: Tim Cottin <[email protected]> Date: Wed, 7 Jan 2026 09:34:50 +0000 Subject: [PATCH 2/6] fix formatting --- clang/include/clang/AST/DeclTemplate.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 6a29265cfb642..b9f86b558ecd0 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -1107,10 +1107,10 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl { SourceRange getSourceRange() const override LLVM_READONLY { SourceLocation BeginLoc = getTemplateParameters()->getTemplateLoc(); if (BeginLoc.isInvalid() && isAbbreviated()) { - // The BeginLoc of FunctionTemplateDecls is derived from the template keyword. - // But "pure" abbreviated templates do not use the template keyword. - // Hence the BeginLoc is invalid. - // Therefore just use the beginning of the templated declaration instead. + // The BeginLoc of FunctionTemplateDecls is derived from the template + // keyword. But "pure" abbreviated templates do not use the template + // keyword. Hence the BeginLoc is invalid. Therefore just use the + // beginning of the templated declaration instead. BeginLoc = getTemplatedDecl()->getBeginLoc(); } >From 943aebb4590bb9d5f5c54b1b9c4c188e87d1bf7b Mon Sep 17 00:00:00 2001 From: Tim Cottin <[email protected]> Date: Wed, 7 Jan 2026 19:56:07 +0000 Subject: [PATCH 3/6] add the source location when creating the template parameter list --- clang/include/clang/AST/DeclTemplate.h | 13 ------------- clang/lib/Sema/SemaDeclCXX.cpp | 8 ++++---- .../ast-dump-record-definition-data-json.cpp | 18 +++--------------- clang/test/CXX/temp/temp.pre/p6.cpp | 2 +- 4 files changed, 8 insertions(+), 33 deletions(-) diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index b9f86b558ecd0..a4a1bb9c13c79 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -1104,19 +1104,6 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl { static FunctionTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); - SourceRange getSourceRange() const override LLVM_READONLY { - SourceLocation BeginLoc = getTemplateParameters()->getTemplateLoc(); - if (BeginLoc.isInvalid() && isAbbreviated()) { - // The BeginLoc of FunctionTemplateDecls is derived from the template - // keyword. But "pure" abbreviated templates do not use the template - // keyword. Hence the BeginLoc is invalid. Therefore just use the - // beginning of the templated declaration instead. - BeginLoc = getTemplatedDecl()->getBeginLoc(); - } - - return SourceRange(BeginLoc, TemplatedDecl->getSourceRange().getEnd()); - } - // Implement isa/cast/dyncast support static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == FunctionTemplate; } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 6b5a80f7ea3f9..51b3f80590d1c 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -19635,10 +19635,10 @@ void Sema::ActOnFinishFunctionDeclarationDeclarator(Declarator &Declarator) { ExplicitParams->getRAngleLoc(), ExplicitParams->getRequiresClause())); } else { - Declarator.setInventedTemplateParameterList( - TemplateParameterList::Create( - Context, SourceLocation(), SourceLocation(), FSI.TemplateParams, - SourceLocation(), /*RequiresClause=*/nullptr)); + Declarator.setInventedTemplateParameterList(TemplateParameterList::Create( + Context, Declarator.getBeginLoc(), SourceLocation(), + FSI.TemplateParams, Declarator.getEndLoc(), + /*RequiresClause=*/nullptr)); } } InventedParameterInfos.pop_back(); diff --git a/clang/test/AST/ast-dump-record-definition-data-json.cpp b/clang/test/AST/ast-dump-record-definition-data-json.cpp index 62acf84e6757c..e35bec78c6847 100644 --- a/clang/test/AST/ast-dump-record-definition-data-json.cpp +++ b/clang/test/AST/ast-dump-record-definition-data-json.cpp @@ -408,11 +408,7 @@ struct DoesNotAllowConstDefaultInit { // CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": { -// CHECK-NEXT: "offset": 197, -// CHECK-NEXT: "col": 33, -// CHECK-NEXT: "tokLen": 1 -// CHECK-NEXT: }, +// CHECK-NEXT: "begin": {}, // CHECK-NEXT: "end": { // CHECK-NEXT: "offset": 199, // CHECK-NEXT: "col": 35, @@ -527,11 +523,7 @@ struct DoesNotAllowConstDefaultInit { // CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": { -// CHECK-NEXT: "offset": 190, -// CHECK-NEXT: "col": 26, -// CHECK-NEXT: "tokLen": 1 -// CHECK-NEXT: }, +// CHECK-NEXT: "begin": {}, // CHECK-NEXT: "end": { // CHECK-NEXT: "offset": 199, // CHECK-NEXT: "col": 35, @@ -606,11 +598,7 @@ struct DoesNotAllowConstDefaultInit { // CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": { -// CHECK-NEXT: "offset": 190, -// CHECK-NEXT: "col": 26, -// CHECK-NEXT: "tokLen": 1 -// CHECK-NEXT: }, +// CHECK-NEXT: "begin": {}, // CHECK-NEXT: "end": { // CHECK-NEXT: "offset": 199, // CHECK-NEXT: "col": 35, diff --git a/clang/test/CXX/temp/temp.pre/p6.cpp b/clang/test/CXX/temp/temp.pre/p6.cpp index 264972eb44eb3..13531483ae1e7 100644 --- a/clang/test/CXX/temp/temp.pre/p6.cpp +++ b/clang/test/CXX/temp/temp.pre/p6.cpp @@ -6,7 +6,7 @@ namespace GH46386 { // CHECK: error: templates must have C++ linkage // CHECK-NEXT: {{^}} void f(auto) {} - // CHECK-NEXT: {{^}} ^~~~~{{$}} + // CHECK-NEXT: {{^}} ^~~~~~~~~~~~{{$}} void f(auto) {} // expected-error {{templates must have C++ linkage}} void f(void) { // expected-note {{candidate function not viable: requires 0 arguments, but 1 was provided}} >From 2a695a192c3b0492f9d4022aa5390c95ba61ffef Mon Sep 17 00:00:00 2001 From: Tim Cottin <[email protected]> Date: Sat, 10 Jan 2026 09:58:43 +0000 Subject: [PATCH 4/6] add valid begin location for generic lambdas --- clang/lib/Sema/SemaLambda.cpp | 2 +- .../ast-dump-record-definition-data-json.cpp | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 81ec0b18dedfd..c8ec49d83acf4 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -235,7 +235,7 @@ getGenericLambdaTemplateParameterList(LambdaScopeInfo *LSI, Sema &SemaRef) { if (!LSI->GLTemplateParameterList && !LSI->TemplateParams.empty()) { LSI->GLTemplateParameterList = TemplateParameterList::Create( SemaRef.Context, - /*Template kw loc*/ SourceLocation(), + /*Template kw loc*/ LSI->IntroducerRange.getBegin(), /*L angle loc*/ LSI->ExplicitTemplateParamsRange.getBegin(), LSI->TemplateParams, /*R angle loc*/LSI->ExplicitTemplateParamsRange.getEnd(), diff --git a/clang/test/AST/ast-dump-record-definition-data-json.cpp b/clang/test/AST/ast-dump-record-definition-data-json.cpp index e35bec78c6847..d8ff6e980fb94 100644 --- a/clang/test/AST/ast-dump-record-definition-data-json.cpp +++ b/clang/test/AST/ast-dump-record-definition-data-json.cpp @@ -408,7 +408,11 @@ struct DoesNotAllowConstDefaultInit { // CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": {}, +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 190, +// CHECK-NEXT: "col": 26, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, // CHECK-NEXT: "end": { // CHECK-NEXT: "offset": 199, // CHECK-NEXT: "col": 35, @@ -523,7 +527,11 @@ struct DoesNotAllowConstDefaultInit { // CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": {}, +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 190, +// CHECK-NEXT: "col": 26, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, // CHECK-NEXT: "end": { // CHECK-NEXT: "offset": 199, // CHECK-NEXT: "col": 35, @@ -598,7 +606,11 @@ struct DoesNotAllowConstDefaultInit { // CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": {}, +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 190, +// CHECK-NEXT: "col": 26, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, // CHECK-NEXT: "end": { // CHECK-NEXT: "offset": 199, // CHECK-NEXT: "col": 35, >From 5afd06483da847186ff988b7ee91d4befa389c76 Mon Sep 17 00:00:00 2001 From: Tim Cottin <[email protected]> Date: Sat, 10 Jan 2026 11:57:01 +0000 Subject: [PATCH 5/6] add hover test with concepts --- .../clangd/unittests/HoverTests.cpp | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp index 1d1da620857fe..7bff20e6f5635 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -118,6 +118,76 @@ TEST(Hover, Structured) { {{"auto"}, std::string("y"), std::nullopt}, }; }}, + {R"cpp( + template<typename T1, typename T2> + concept C = requires () { true; }; + + // Best foo ever. + template<C<int> T> + void [[fo^o]](T x) {} + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "foo"; + HI.Kind = index::SymbolKind::Function; + HI.Documentation = "Best foo ever."; + HI.Definition = "template <C<int> T> void foo(T x)"; + HI.ReturnType = "void"; + HI.Type = "void (T)"; + HI.TemplateParameters = { + {{"class"}, std::string("T"), std::nullopt}, + }; + HI.Parameters = { + {{"T"}, std::string("x"), std::nullopt}, + }; + }}, + {R"cpp( + template<typename T1, typename T2> + concept C = requires () { true; }; + + // Best foo ever. + void [[fo^o]](C<int> auto x) {} + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "foo"; + HI.Kind = index::SymbolKind::Function; + HI.Documentation = "Best foo ever."; + HI.Definition = "void foo(C<int> auto x)"; + HI.ReturnType = "void"; + HI.Type = "void (C<int> auto)"; + HI.TemplateParameters = { + {{"class"}, std::string("x:auto"), std::nullopt}, + }; + HI.Parameters = { + {{"C<int> auto"}, std::string("x"), std::nullopt}, + }; + }}, + {R"cpp( + template<typename T1, typename T2> + concept C = requires () { true; }; + + // Best foo ever. + template<C<int> T> + void [[fo^o]](T x, C<int> auto y) {} + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "foo"; + HI.Kind = index::SymbolKind::Function; + HI.Documentation = "Best foo ever."; + HI.Definition = "template <C<int> T> void foo(T x, C<int> auto y)"; + HI.ReturnType = "void"; + HI.Type = "void (T, C<int> auto)"; + HI.TemplateParameters = { + {{"class"}, std::string("T"), std::nullopt}, + {{"class"}, std::string("y:auto"), std::nullopt}, + }; + HI.Parameters = { + {{"T"}, std::string("x"), std::nullopt}, + {{"C<int> auto"}, std::string("y"), std::nullopt}, + }; + }}, // Inside namespace {R"cpp( namespace ns1 { namespace ns2 { >From 12b7d861c0c977fbf4db2430129ef88e26b1b7ae Mon Sep 17 00:00:00 2001 From: Tim Cottin <[email protected]> Date: Sat, 10 Jan 2026 12:08:50 +0000 Subject: [PATCH 6/6] add release note --- clang/docs/ReleaseNotes.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 8f7a0a2b304d0..a2fb93d5d7073 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -134,6 +134,12 @@ ABI Changes in This Version --------------------------- - Fix AArch64 argument passing for C++ empty classes with large explicitly specified alignment. +AST Potentially Breaking Changes +-------------------------------- +- Abbreviated function templates and generic lambdas now have a valid begin source location. + The begin source location of abbreviated function templates is the begin source location of the templated function. + The begin source location of generic lambdas is the begin source location of the lambda introducer ``[...]``. + AST Dumping Potentially Breaking Changes ---------------------------------------- - How nested name specifiers are dumped and printed changes, keeping track of clang AST changes. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
