[clang] [clang-tools-extra] [clang] Add a valid begin source location for abbreviated function templates (PR #174723)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Jan 10 04:10:07 PST 2026
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 <timcottin at gmx.de>
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 <timcottin at gmx.de>
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 <timcottin at gmx.de>
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 <timcottin at gmx.de>
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 <timcottin at gmx.de>
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 <timcottin at gmx.de>
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.
More information about the cfe-commits
mailing list