[Index] Reflect in SymbolSubKind whether a typedef points to a struct or a class (#181967)
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
This commit is contained in:
parent
5b9b25b9f8
commit
28d564bcc9
@ -350,7 +350,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();
|
||||
@ -431,7 +431,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);
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -1814,7 +1814,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);
|
||||
@ -1871,7 +1871,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).
|
||||
|
||||
@ -116,6 +116,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(
|
||||
|
||||
@ -79,6 +79,8 @@ enum class SymbolSubKind : uint8_t {
|
||||
UsingTypename,
|
||||
UsingValue,
|
||||
UsingEnum,
|
||||
UsingClass,
|
||||
UsingStruct,
|
||||
};
|
||||
|
||||
typedef uint16_t SymbolPropertySet;
|
||||
|
||||
@ -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,23 @@ 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;
|
||||
}
|
||||
}
|
||||
// Not a tag type, e.g. typedef for a builtin type.
|
||||
return SymbolSubKind::None;
|
||||
}
|
||||
|
||||
SymbolInfo index::getSymbolInfo(const Decl *D) {
|
||||
assert(D);
|
||||
SymbolInfo Info;
|
||||
@ -171,8 +190,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,10 +332,12 @@ 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;
|
||||
@ -553,9 +577,16 @@ 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::UsingValue: return "using-value";
|
||||
case SymbolSubKind::UsingEnum: return "using-enum";
|
||||
case SymbolSubKind::UsingTypename:
|
||||
return "using-typename";
|
||||
case SymbolSubKind::UsingValue:
|
||||
return "using-value";
|
||||
case SymbolSubKind::UsingEnum:
|
||||
return "using-enum";
|
||||
case SymbolSubKind::UsingClass:
|
||||
return "using-class";
|
||||
case SymbolSubKind::UsingStruct:
|
||||
return "using-struct";
|
||||
}
|
||||
llvm_unreachable("invalid symbol subkind");
|
||||
}
|
||||
|
||||
@ -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]]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user