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/7] [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/7] 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/7] 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/7] 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/7] 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/7] 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.

>From 8106981c95731337f78dc07ff545263cf8534084 Mon Sep 17 00:00:00 2001
From: Tim Cottin <[email protected]>
Date: Sat, 10 Jan 2026 12:14:20 +0000
Subject: [PATCH 7/7] fix formatting

---
 clang/lib/Sema/SemaLambda.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index c8ec49d83acf4..9a1b5a9e688a7 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -238,7 +238,7 @@ getGenericLambdaTemplateParameterList(LambdaScopeInfo *LSI, 
Sema &SemaRef) {
         /*Template kw loc*/ LSI->IntroducerRange.getBegin(),
         /*L angle loc*/ LSI->ExplicitTemplateParamsRange.getBegin(),
         LSI->TemplateParams,
-        /*R angle loc*/LSI->ExplicitTemplateParamsRange.getEnd(),
+        /*R angle loc*/ LSI->ExplicitTemplateParamsRange.getEnd(),
         LSI->RequiresClause.get());
   }
   return LSI->GLTemplateParameterList;

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to