[clangd] Respect ArgumentLists for struct templates (#212094)

Also fix a bug in index::getSymbolKind where the returned symbol kind
would be Class for a struct template.

Fixes https://github.com/clangd/clangd/issues/2684
This commit is contained in:
Nathan Ridge 2026-07-26 21:07:00 -04:00 committed by GitHub
parent e4205abdad
commit e9240abc52
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 40 additions and 7 deletions

View file

@ -643,6 +643,7 @@ private:
// 'CompletionItemKind::Interface' matches template type aliases.
if (Completion.Kind == CompletionItemKind::Interface ||
Completion.Kind == CompletionItemKind::Class ||
Completion.Kind == CompletionItemKind::Struct ||
Completion.Kind == CompletionItemKind::Variable) {
if (Snippet->front() != '<')
return *Snippet; // Not an arg snippet?

View file

@ -841,7 +841,7 @@ TEST(CompletionTest, Kinds) {
EXPECT_THAT(
Results.Completions,
UnorderedElementsAre(
AllOf(named("complete_class"), kind(CompletionItemKind::Class)),
AllOf(named("complete_class"), kind(CompletionItemKind::Struct)),
AllOf(named("complete_function"), kind(CompletionItemKind::Function)),
AllOf(named("complete_type_alias"),
kind(CompletionItemKind::Interface)),
@ -2996,6 +2996,8 @@ TEST(CompletionTest, ArgumentListsPolicy) {
template <class T>
class foo_class{};
template <class T>
struct foo_struct{};
template <class T>
using foo_alias = T**;
template <class T>
T foo_var = T{};
@ -3004,9 +3006,35 @@ TEST(CompletionTest, ArgumentListsPolicy) {
EXPECT_THAT(
Results.Completions,
UnorderedElementsAre(AllOf(named("foo_class"), snippetSuffix("<$0>")),
AllOf(named("foo_struct"), snippetSuffix("<$0>")),
AllOf(named("foo_alias"), snippetSuffix("<$0>")),
AllOf(named("foo_var"), snippetSuffix("<$0>"))));
}
{
const char *Header = R"cpp(
template <class T>
class foo_class{};
template <class T>
struct foo_struct{};
template <class T>
using foo_alias = T**;
template <class T>
T foo_var = T{};
)cpp";
auto Index = TestTU::withHeaderCode(Header).index();
Opts.Index = Index.get();
auto Results = completions(
R"cpp(
void f() { foo_^ })cpp",
{}, Opts);
EXPECT_THAT(
Results.Completions,
UnorderedElementsAre(AllOf(named("foo_class"), snippetSuffix("<$0>")),
AllOf(named("foo_struct"), snippetSuffix("<$0>")),
AllOf(named("foo_alias"), snippetSuffix("<$0>")),
AllOf(named("foo_var"), snippetSuffix("<$0>"))));
Opts.Index = nullptr;
}
{
auto Results = completions(
R"cpp(

View file

@ -302,11 +302,15 @@ SymbolInfo index::getSymbolInfo(const Decl *D) {
Info.Lang = SymbolLanguage::CXX;
break;
}
case Decl::ClassTemplate:
Info.Kind = SymbolKind::Class;
case Decl::ClassTemplate: {
const ClassTemplateDecl *CTD = cast<ClassTemplateDecl>(D);
Info.Kind = CTD->getTemplatedDecl()->getTagKind() == TagTypeKind::Struct
? SymbolKind::Struct
: SymbolKind::Class;
Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic;
Info.Lang = SymbolLanguage::CXX;
break;
}
case Decl::FunctionTemplate:
Info.Kind = SymbolKind::Function;
Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic;

View file

@ -105,7 +105,7 @@ int ginitlist[] = {EnumVal};
// CHECK-NEXT: [indexEntityReference]: kind: namespace | name: NS | {{.*}} | loc: 44:7
// CHECK-NEXT: [indexEntityReference]: kind: typedef | name: Foo | {{.*}} | loc: 44:11
// CHECK: [indexDeclaration]: kind: c++-class-template | name: TS | {{.*}} | loc: 47:8
// CHECK: [indexDeclaration]: kind: struct-template | name: TS | {{.*}} | loc: 47:8
// CHECK-NEXT: [indexDeclaration]: kind: struct-template-partial-spec | name: TS | USR: c:@SP>1#T@TS>#t0.0#I | {{.*}} | loc: 50:8
// CHECK-NEXT: [indexDeclaration]: kind: typedef | name: MyInt | USR: c:index-refs.cpp@SP>1#T@TS>#t0.0#I@T@MyInt | {{.*}} | loc: 51:15 | semantic-container: [TS:50:8] | lexical-container: [TS:50:8]
/* when indexing implicit instantiations

View file

@ -24,6 +24,6 @@ struct D : B<T> {};
// CHECK-NEXT: [indexEntityReference]: kind: c++-class-template | name: Foo | USR: c:@ST>1#T@Foo
// CHECK-NEXT: [indexEntityReference]: kind: c++-instance-method | name: f | USR: c:@ST>1#T@Foo@F@f#t0.0#
// CHECK: [indexDeclaration]: kind: c++-class-template | name: D
// CHECK-NEXT: <base>: kind: c++-class-template | name: B
// CHECK-NEXT: [indexEntityReference]: kind: c++-class-template | name: B
// CHECK: [indexDeclaration]: kind: struct-template | name: D
// CHECK-NEXT: <base>: kind: struct-template | name: B
// CHECK-NEXT: [indexEntityReference]: kind: struct-template | name: B