https://github.com/HighCommander4 updated https://github.com/llvm/llvm-project/pull/181967
>From 49f4c893c165572a3d5f486fc50dae9c95c51f97 Mon Sep 17 00:00:00 2001 From: Nathan Ridge <[email protected]> Date: Wed, 18 Feb 2026 01:38:36 -0500 Subject: [PATCH] [Index] Reflect in SymbolSubKind whether a typedef points to a struct or a class Typedefs don't have their own symbol kind in the Language Server Protocol, the choices are Struct or Class. For clangd to be able to represent typedefs accurately in response to requests such as workspace/symbol, it needs this information surfaced in index::SymbolInfo. Fixes https://github.com/clangd/clangd/issues/2253 --- clang-tools-extra/clangd/FindSymbols.cpp | 4 +-- clang-tools-extra/clangd/Protocol.cpp | 16 ++++++--- clang-tools-extra/clangd/Protocol.h | 2 +- clang-tools-extra/clangd/XRefs.cpp | 4 +-- .../clangd/unittests/FindSymbolsTests.cpp | 22 ++++++++++++ clang/include/clang/Index/IndexSymbol.h | 3 +- clang/lib/Index/IndexSymbol.cpp | 36 ++++++++++++++++--- .../Index/Core/index-dependent-source.cpp | 4 +-- clang/test/Index/Core/index-source.cpp | 4 +-- 9 files changed, 76 insertions(+), 19 deletions(-) diff --git a/clang-tools-extra/clangd/FindSymbols.cpp b/clang-tools-extra/clangd/FindSymbols.cpp index 7655a39d5ba1f..4afc7536df034 100644 --- a/clang-tools-extra/clangd/FindSymbols.cpp +++ b/clang-tools-extra/clangd/FindSymbols.cpp @@ -152,7 +152,7 @@ getWorkspaceSymbols(llvm::StringRef Query, int Limit, SymbolInformation Info; Info.name = (Sym.Name + Sym.TemplateSpecializationArgs).str(); - Info.kind = indexSymbolKindToSymbolKind(Sym.SymInfo.Kind); + Info.kind = indexSymbolKindToSymbolKind(Sym.SymInfo); Info.location = *Loc; Scope.consume_back("::"); Info.containerName = Scope.str(); @@ -233,7 +233,7 @@ std::optional<DocumentSymbol> declToSym(ASTContext &Ctx, const NamedDecl &ND) { index::SymbolInfo SymInfo = index::getSymbolInfo(&ND); // FIXME: This is not classifying constructors, destructors and operators // correctly. - SymbolKind SK = indexSymbolKindToSymbolKind(SymInfo.Kind); + SymbolKind SK = indexSymbolKindToSymbolKind(SymInfo); DocumentSymbol SI; SI.name = getSymbolName(Ctx, ND); diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp index 560b8e00ed377..6860a7e40ad57 100644 --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -295,8 +295,8 @@ SymbolKind adjustKindToCapability(SymbolKind Kind, } } -SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind) { - switch (Kind) { +SymbolKind indexSymbolKindToSymbolKind(const index::SymbolInfo &Info) { + switch (Info.Kind) { // FIXME: for backwards compatibility, the include directive kind is treated // the same as Unknown case index::SymbolKind::IncludeDirective: @@ -322,8 +322,16 @@ SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind) { return SymbolKind::Interface; case index::SymbolKind::Union: return SymbolKind::Class; - case index::SymbolKind::TypeAlias: - return SymbolKind::Class; + case index::SymbolKind::TypeAlias: { + switch (Info.SubKind) { + case index::SymbolSubKind::UsingStruct: + return SymbolKind::Struct; + case index::SymbolSubKind::UsingClass: + return SymbolKind::Class; + default: + return SymbolKind::Class; + } + } case index::SymbolKind::Function: return SymbolKind::Function; case index::SymbolKind::Variable: diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h index 2248572060431..128850491ef0c 100644 --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -417,7 +417,7 @@ SymbolKind adjustKindToCapability(SymbolKind Kind, // Note, some are not perfect matches and should be improved when this LSP // issue is addressed: // https://github.com/Microsoft/language-server-protocol/issues/344 -SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind); +SymbolKind indexSymbolKindToSymbolKind(const index::SymbolInfo &Info); // Determines the encoding used to measure offsets and lengths of source in LSP. enum class OffsetEncoding { diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index 8a24d19a7d129..efd76fe1d76ae 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -1806,7 +1806,7 @@ declToHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) { index::SymbolInfo SymInfo = index::getSymbolInfo(&ND); // FIXME: This is not classifying constructors, destructors and operators // correctly. - SymbolKind SK = indexSymbolKindToSymbolKind(SymInfo.Kind); + SymbolKind SK = indexSymbolKindToSymbolKind(SymInfo); HierarchyItem HI; HI.name = printName(Ctx, ND); @@ -1863,7 +1863,7 @@ static std::optional<HierarchyItem> symbolToHierarchyItem(const Symbol &S, HierarchyItem HI; HI.name = std::string(S.Name); HI.detail = (S.Scope + S.Name).str(); - HI.kind = indexSymbolKindToSymbolKind(S.SymInfo.Kind); + HI.kind = indexSymbolKindToSymbolKind(S.SymInfo); HI.selectionRange = Loc->range; // FIXME: Populate 'range' correctly // (https://github.com/clangd/clangd/issues/59). diff --git a/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp b/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp index d6e94bf752452..6a6c87c70317a 100644 --- a/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp @@ -110,6 +110,28 @@ TEST(WorkspaceSymbols, Unnamed) { withKind(SymbolKind::Field)))); } +TEST(WorkspaceSymbols, TypeAlias) { + TestTU TU; + TU.Code = R"cpp( + struct Struct {}; + class Class {}; + class Container { + using StructAlias = Struct; + using ClassAlias = Class; + }; + )cpp"; + EXPECT_THAT( + getSymbols(TU, "Struct"), + UnorderedElementsAre(AllOf(qName("Struct"), withKind(SymbolKind::Struct)), + AllOf(qName("Container::StructAlias"), + withKind(SymbolKind::Struct)))); + EXPECT_THAT( + getSymbols(TU, "Class"), + UnorderedElementsAre( + AllOf(qName("Class"), withKind(SymbolKind::Class)), + AllOf(qName("Container::ClassAlias"), withKind(SymbolKind::Class)))); +} + TEST(WorkspaceSymbols, InMainFile) { TestTU TU; TU.Code = R"cpp( diff --git a/clang/include/clang/Index/IndexSymbol.h b/clang/include/clang/Index/IndexSymbol.h index deb9337d9d1a1..1b1291b3349c6 100644 --- a/clang/include/clang/Index/IndexSymbol.h +++ b/clang/include/clang/Index/IndexSymbol.h @@ -76,7 +76,8 @@ enum class SymbolSubKind : uint8_t { CXXMoveConstructor, AccessorGetter, AccessorSetter, - UsingTypename, + UsingClass, + UsingStruct, UsingValue, UsingEnum, }; diff --git a/clang/lib/Index/IndexSymbol.cpp b/clang/lib/Index/IndexSymbol.cpp index c3cbc03cf9b41..a167bc9faa076 100644 --- a/clang/lib/Index/IndexSymbol.cpp +++ b/clang/lib/Index/IndexSymbol.cpp @@ -8,10 +8,12 @@ #include "clang/Index/IndexSymbol.h" #include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/PrettyPrinter.h" +#include "clang/AST/TypeBase.h" #include "clang/Lex/MacroInfo.h" using namespace clang; @@ -83,6 +85,22 @@ bool index::isFunctionLocalSymbol(const Decl *D) { return true; } +static SymbolSubKind getSubKindForTypedef(const TypedefNameDecl *TND) { + if (const TagDecl *TD = TND->getUnderlyingType()->getAsTagDecl()) { + switch (TD->getTagKind()) { + case TagTypeKind::Class: + return SymbolSubKind::UsingClass; + case TagTypeKind::Struct: + return SymbolSubKind::UsingStruct; + default: + // Leave SymbolSubKind blank. + // New subkinds like UsingUnion can be added if/when needed. + return SymbolSubKind::None; + } + } + return SymbolSubKind::None; +} + SymbolInfo index::getSymbolInfo(const Decl *D) { assert(D); SymbolInfo Info; @@ -171,8 +189,11 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { case Decl::Import: Info.Kind = SymbolKind::Module; break; - case Decl::Typedef: - Info.Kind = SymbolKind::TypeAlias; break; // Lang = C + case Decl::Typedef: { + Info.Kind = SymbolKind::TypeAlias; // Lang = C + Info.SubKind = getSubKindForTypedef(cast<TypedefDecl>(D)); + break; + } case Decl::Function: Info.Kind = SymbolKind::Function; break; @@ -310,13 +331,15 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { Info.Lang = SymbolLanguage::CXX; Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; break; - case Decl::TypeAlias: + case Decl::TypeAlias: { Info.Kind = SymbolKind::TypeAlias; + Info.SubKind = getSubKindForTypedef(cast<TypeAliasDecl>(D)); Info.Lang = SymbolLanguage::CXX; break; + } case Decl::UnresolvedUsingTypename: Info.Kind = SymbolKind::Using; - Info.SubKind = SymbolSubKind::UsingTypename; + Info.SubKind = SymbolSubKind::UsingClass; Info.Lang = SymbolLanguage::CXX; Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; break; @@ -553,7 +576,10 @@ StringRef index::getSymbolSubKindString(SymbolSubKind K) { case SymbolSubKind::CXXMoveConstructor: return "cxx-move-ctor"; case SymbolSubKind::AccessorGetter: return "acc-get"; case SymbolSubKind::AccessorSetter: return "acc-set"; - case SymbolSubKind::UsingTypename: return "using-typename"; + case SymbolSubKind::UsingClass: + return "using-class"; + case SymbolSubKind::UsingStruct: + return "using-struct"; case SymbolSubKind::UsingValue: return "using-value"; case SymbolSubKind::UsingEnum: return "using-enum"; } diff --git a/clang/test/Index/Core/index-dependent-source.cpp b/clang/test/Index/Core/index-dependent-source.cpp index ef414c8fdf7a0..b718ae4a831df 100644 --- a/clang/test/Index/Core/index-dependent-source.cpp +++ b/clang/test/Index/Core/index-dependent-source.cpp @@ -179,7 +179,7 @@ struct UsingB : public UsingA<T> { // CHECK: [[@LINE+2]]:40 | type-alias/C | TypeB | c:index-dependent-source.cpp@ST>1#T@UsingB@T@TypeB | <no-cgname> | Def,RelChild | rel: 1 // CHECK: [[@LINE+1]]:20 | struct(Gen)/C++ | OtherUsing | c:@ST>1#T@OtherUsing | <no-cgname> | Ref,RelCont | rel: 1 typedef typename OtherUsing<T>::Type TypeB; -// CHECK: [[@LINE+2]]:29 | using/using-typename(Gen)/C++ | Type | c:index-dependent-source.cpp@ST>1#T@UsingB@UUT@UsingA<T>::Type | <no-cgname> | Decl,RelChild | rel: 1 +// CHECK: [[@LINE+2]]:29 | using/using-class(Gen)/C++ | Type | c:index-dependent-source.cpp@ST>1#T@UsingB@UUT@UsingA<T>::Type | <no-cgname> | Decl,RelChild | rel: 1 // CHECK: [[@LINE+1]]:18 | struct(Gen)/C++ | UsingA | c:@ST>1#T@UsingA | <no-cgname> | Ref,RelCont | rel: 1 using typename UsingA<T>::Type; // CHECK: [[@LINE+2]]:20 | using/using-value(Gen)/C++ | func | c:index-dependent-source.cpp@ST>1#T@UsingB@UUV@UsingA<T>::func | <no-cgname> | Decl,RelChild | rel: 1 @@ -200,7 +200,7 @@ struct UsingC : public UsingB<T> { // CHECK: [[@LINE+2]]:25 | type-alias/C | TypeB | c:index-dependent-source.cpp@ST>1#T@UsingB@T@TypeB | <no-cgname> | Ref,RelCont | rel: 1 // CHECK: [[@LINE+1]]:14 | struct(Gen)/C++ | UsingB | c:@ST>1#T@UsingB | <no-cgname> | Ref,RelCont | rel: 1 typename UsingB<T>::TypeB value1; -// CHECK: [[@LINE+2]]:25 | using/using-typename(Gen)/C++ | Type | c:index-dependent-source.cpp@ST>1#T@UsingB@UUT@UsingA<T>::Type | <no-cgname> | Ref,RelCont | rel: 1 +// CHECK: [[@LINE+2]]:25 | using/using-class(Gen)/C++ | Type | c:index-dependent-source.cpp@ST>1#T@UsingB@UUT@UsingA<T>::Type | <no-cgname> | Ref,RelCont | rel: 1 // CHECK: [[@LINE+1]]:14 | struct(Gen)/C++ | UsingB | c:@ST>1#T@UsingB | <no-cgname> | Ref,RelCont | rel: 1 typename UsingB<T>::Type value2; // CHECK: [[@LINE+2]]:16 | using/using-value(Gen)/C++ | func | c:index-dependent-source.cpp@ST>1#T@UsingB@UUV@UsingA<T>::func | <no-cgname> | Ref,Call,RelCall,RelCont | rel: 1 diff --git a/clang/test/Index/Core/index-source.cpp b/clang/test/Index/Core/index-source.cpp index 36bc663b89684..e376523a58f7c 100644 --- a/clang/test/Index/Core/index-source.cpp +++ b/clang/test/Index/Core/index-source.cpp @@ -23,10 +23,10 @@ class Cls { public: // CHECK: [[@LINE+2]]:24 | class/C++ | Cls | [[Cls_USR]] | <no-cgname> | Ref,RelBase,RelCont | rel: 1 // CHECK-NEXT: RelBase,RelCont | SubCls1 | [[SubCls1_USR]] class SubCls1 : public Cls {}; -// CHECK: [[@LINE+1]]:13 | type-alias/C | ClsAlias | [[ClsAlias_USR:.*]] | <no-cgname> | Def | rel: 0 +// CHECK: [[@LINE+1]]:13 | type-alias/using-class/C | ClsAlias | [[ClsAlias_USR:.*]] | <no-cgname> | Def | rel: 0 typedef Cls ClsAlias; // CHECK: [[@LINE+5]]:7 | class/C++ | SubCls2 | [[SubCls2_USR:.*]] | <no-cgname> | Def | rel: 0 -// CHECK: [[@LINE+4]]:24 | type-alias/C | ClsAlias | [[ClsAlias_USR]] | <no-cgname> | Ref,RelCont | rel: 1 +// CHECK: [[@LINE+4]]:24 | type-alias/using-class/C | ClsAlias | [[ClsAlias_USR]] | <no-cgname> | Ref,RelCont | rel: 1 // CHECK-NEXT: RelCont | SubCls2 | [[SubCls2_USR]] // CHECK: [[@LINE+2]]:24 | class/C++ | Cls | [[Cls_USR]] | <no-cgname> | Ref,Impl,RelBase,RelCont | rel: 1 // CHECK-NEXT: RelBase,RelCont | SubCls2 | [[SubCls2_USR]] _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
