From 3eceee44f5c934a3eb1c9703358cd9d46d8c1fde Mon Sep 17 00:00:00 2001 From: Nathan Ridge Date: Tue, 2 Sep 2025 01:06:07 -0400 Subject: [PATCH] [clang][HeuristicResolver] Default argument heuristic for template template parameters Fixes https://github.com/clangd/clangd/issues/2478 --- clang/lib/Sema/HeuristicResolver.cpp | 19 +++++++++++++++++++ .../unittests/Sema/HeuristicResolverTest.cpp | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/clang/lib/Sema/HeuristicResolver.cpp b/clang/lib/Sema/HeuristicResolver.cpp index a7fe4ea28b72c..cbdefaa57aacb 100644 --- a/clang/lib/Sema/HeuristicResolver.cpp +++ b/clang/lib/Sema/HeuristicResolver.cpp @@ -259,6 +259,25 @@ QualType HeuristicResolverImpl::simplifyType(QualType Type, const Expr *E, } } } + + // Similarly, heuristically replace a template template parameter with its + // default argument if it has one. + if (const auto *TST = + dyn_cast_if_present(T.Type)) { + if (const auto *TTPD = dyn_cast_if_present( + TST->getTemplateName().getAsTemplateDecl())) { + if (TTPD->hasDefaultArgument()) { + const auto &DefaultArg = TTPD->getDefaultArgument().getArgument(); + if (DefaultArg.getKind() == TemplateArgument::Template) { + if (const auto *CTD = dyn_cast_if_present( + DefaultArg.getAsTemplate().getAsTemplateDecl())) { + return {Ctx.getCanonicalTagType(CTD->getTemplatedDecl())}; + } + } + } + } + } + // Check if the expression refers to an explicit object parameter of // templated type. If so, heuristically treat it as having the type of the // enclosing class. diff --git a/clang/unittests/Sema/HeuristicResolverTest.cpp b/clang/unittests/Sema/HeuristicResolverTest.cpp index 0eb14f032f16c..a00632f232ea5 100644 --- a/clang/unittests/Sema/HeuristicResolverTest.cpp +++ b/clang/unittests/Sema/HeuristicResolverTest.cpp @@ -558,6 +558,24 @@ TEST(HeuristicResolver, MemberExpr_DefaultTemplateArgument_Recursive) { cxxMethodDecl(hasName("foo")).bind("output")); } +TEST(HeuristicResolver, MemberExpr_DefaultTemplateTemplateArgument) { + std::string Code = R"cpp( + template + struct vector { + void push_back(T); + }; + template class Container = vector> + void foo(Container c, Element e) { + c.push_back(e); + } + )cpp"; + // Test resolution of "push_back" in "c.push_back(e)". + expectResolution( + Code, &HeuristicResolver::resolveMemberExpr, + cxxDependentScopeMemberExpr(hasMemberName("push_back")).bind("input"), + cxxMethodDecl(hasName("push_back")).bind("output")); +} + TEST(HeuristicResolver, MemberExpr_ExplicitObjectParameter) { std::string Code = R"cpp( struct Foo {