forked from OSchip/llvm-project
[clangd] Improve goToDefinition on auto and dectype
locateSymbolAt (used in goToDeclaration) follows the deduced type instead of failing to locate the declaration. Reviewed By: sammccall Differential Revision: https://reviews.llvm.org/D92977
This commit is contained in:
parent
ebef92169c
commit
bda7d0af97
|
@ -463,6 +463,42 @@ locateASTReferent(SourceLocation CurLoc, const syntax::Token *TouchedIdentifier,
|
|||
return Result;
|
||||
}
|
||||
|
||||
std::vector<LocatedSymbol> locateSymbolForType(const ParsedAST &AST,
|
||||
const QualType &Type) {
|
||||
const auto &SM = AST.getSourceManager();
|
||||
auto MainFilePath =
|
||||
getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
|
||||
if (!MainFilePath) {
|
||||
elog("Failed to get a path for the main file, so no symbol.");
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Decls = targetDecl(DynTypedNode::create(Type.getNonReferenceType()),
|
||||
DeclRelation::TemplatePattern | DeclRelation::Alias);
|
||||
if (Decls.empty())
|
||||
return {};
|
||||
|
||||
std::vector<LocatedSymbol> Results;
|
||||
const auto &ASTContext = AST.getASTContext();
|
||||
|
||||
for (const NamedDecl *D : Decls) {
|
||||
D = getPreferredDecl(D);
|
||||
|
||||
auto Loc = makeLocation(ASTContext, nameLocation(*D, SM), *MainFilePath);
|
||||
if (!Loc)
|
||||
continue;
|
||||
|
||||
Results.emplace_back();
|
||||
Results.back().Name = printName(ASTContext, *D);
|
||||
Results.back().PreferredDeclaration = *Loc;
|
||||
if (const NamedDecl *Def = getDefinition(D))
|
||||
Results.back().Definition =
|
||||
makeLocation(ASTContext, nameLocation(*Def, SM), *MainFilePath);
|
||||
}
|
||||
|
||||
return Results;
|
||||
}
|
||||
|
||||
bool tokenSpelledAt(SourceLocation SpellingLoc, const syntax::TokenBuffer &TB) {
|
||||
auto ExpandedTokens = TB.expandedTokens(
|
||||
TB.sourceManager().getMacroArgExpandedLocation(SpellingLoc));
|
||||
|
@ -707,15 +743,31 @@ std::vector<LocatedSymbol> locateSymbolAt(ParsedAST &AST, Position Pos,
|
|||
return {};
|
||||
}
|
||||
|
||||
const syntax::Token *TouchedIdentifier =
|
||||
syntax::spelledIdentifierTouching(*CurLoc, AST.getTokens());
|
||||
if (TouchedIdentifier)
|
||||
if (auto Macro =
|
||||
locateMacroReferent(*TouchedIdentifier, AST, *MainFilePath))
|
||||
// Don't look at the AST or index if we have a macro result.
|
||||
// (We'd just return declarations referenced from the macro's
|
||||
// expansion.)
|
||||
return {*std::move(Macro)};
|
||||
const syntax::Token *TouchedIdentifier = nullptr;
|
||||
auto TokensTouchingCursor =
|
||||
syntax::spelledTokensTouching(*CurLoc, AST.getTokens());
|
||||
for (const syntax::Token &Tok : TokensTouchingCursor) {
|
||||
if (Tok.kind() == tok::identifier) {
|
||||
if (auto Macro = locateMacroReferent(Tok, AST, *MainFilePath))
|
||||
// Don't look at the AST or index if we have a macro result.
|
||||
// (We'd just return declarations referenced from the macro's
|
||||
// expansion.)
|
||||
return {*std::move(Macro)};
|
||||
|
||||
TouchedIdentifier = &Tok;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Tok.kind() == tok::kw_auto || Tok.kind() == tok::kw_decltype) {
|
||||
// go-to-definition on auto should find the definition of the deduced
|
||||
// type, if possible
|
||||
if (auto Deduced = getDeducedType(AST.getASTContext(), Tok.location())) {
|
||||
auto LocSym = locateSymbolForType(AST, *Deduced);
|
||||
if (!LocSym.empty())
|
||||
return LocSym;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ASTNodeKind NodeKind;
|
||||
auto ASTResults = locateASTReferent(*CurLoc, TouchedIdentifier, AST,
|
||||
|
|
|
@ -26,23 +26,168 @@ namespace clang {
|
|||
namespace clangd {
|
||||
namespace {
|
||||
|
||||
TEST(GetDeducedType, KwAutoExpansion) {
|
||||
TEST(GetDeducedType, KwAutoKwDecltypeExpansion) {
|
||||
struct Test {
|
||||
StringRef AnnotatedCode;
|
||||
const char *DeducedType;
|
||||
} Tests[] = {
|
||||
{"^auto i = 0;", "int"},
|
||||
{"^auto f(){ return 1;};", "int"},
|
||||
{
|
||||
R"cpp( // auto on struct in a namespace
|
||||
namespace ns1 { struct S {}; }
|
||||
^auto v = ns1::S{};
|
||||
)cpp",
|
||||
"struct ns1::S",
|
||||
},
|
||||
{
|
||||
R"cpp( // decltype on struct
|
||||
namespace ns1 { struct S {}; }
|
||||
ns1::S i;
|
||||
^decltype(i) j;
|
||||
)cpp",
|
||||
"ns1::S",
|
||||
},
|
||||
{
|
||||
R"cpp(// decltype(auto) on struct&
|
||||
namespace ns1 {
|
||||
struct S {};
|
||||
} // namespace ns1
|
||||
|
||||
ns1::S i;
|
||||
ns1::S& j = i;
|
||||
^decltype(auto) k = j;
|
||||
)cpp",
|
||||
"struct ns1::S &",
|
||||
},
|
||||
{
|
||||
R"cpp( // auto on template class
|
||||
class X;
|
||||
template<typename T> class Foo {};
|
||||
^auto v = Foo<X>();
|
||||
)cpp",
|
||||
"class Foo<class X>",
|
||||
},
|
||||
{
|
||||
R"cpp( // auto on initializer list.
|
||||
namespace std
|
||||
{
|
||||
template<class _E>
|
||||
class [[initializer_list]] {};
|
||||
}
|
||||
|
||||
^auto i = {1,2};
|
||||
)cpp",
|
||||
"class std::initializer_list<int>",
|
||||
},
|
||||
{
|
||||
R"cpp( // auto in function return type with trailing return type
|
||||
struct Foo {};
|
||||
^auto test() -> decltype(Foo()) {
|
||||
return Foo();
|
||||
}
|
||||
)cpp",
|
||||
"struct Foo",
|
||||
},
|
||||
{
|
||||
R"cpp( // decltype in trailing return type
|
||||
struct Foo {};
|
||||
auto test() -> ^decltype(Foo()) {
|
||||
return Foo();
|
||||
}
|
||||
)cpp",
|
||||
"struct Foo",
|
||||
},
|
||||
{
|
||||
R"cpp( // auto in function return type
|
||||
struct Foo {};
|
||||
^auto test() {
|
||||
return Foo();
|
||||
}
|
||||
)cpp",
|
||||
"struct Foo",
|
||||
},
|
||||
{
|
||||
R"cpp( // auto& in function return type
|
||||
struct Foo {};
|
||||
^auto& test() {
|
||||
static Foo x;
|
||||
return x;
|
||||
}
|
||||
)cpp",
|
||||
"struct Foo",
|
||||
},
|
||||
{
|
||||
R"cpp( // auto* in function return type
|
||||
struct Foo {};
|
||||
^auto* test() {
|
||||
Foo *x;
|
||||
return x;
|
||||
}
|
||||
)cpp",
|
||||
"struct Foo",
|
||||
},
|
||||
{
|
||||
R"cpp( // const auto& in function return type
|
||||
struct Foo {};
|
||||
const ^auto& test() {
|
||||
static Foo x;
|
||||
return x;
|
||||
}
|
||||
)cpp",
|
||||
"struct Foo",
|
||||
},
|
||||
{
|
||||
R"cpp( // decltype(auto) in function return (value)
|
||||
struct Foo {};
|
||||
^decltype(auto) test() {
|
||||
return Foo();
|
||||
}
|
||||
)cpp",
|
||||
"struct Foo",
|
||||
},
|
||||
{
|
||||
R"cpp( // decltype(auto) in function return (ref)
|
||||
struct Foo {};
|
||||
^decltype(auto) test() {
|
||||
static Foo x;
|
||||
return (x);
|
||||
}
|
||||
)cpp",
|
||||
"struct Foo &",
|
||||
},
|
||||
{
|
||||
R"cpp( // decltype(auto) in function return (const ref)
|
||||
struct Foo {};
|
||||
^decltype(auto) test() {
|
||||
static const Foo x;
|
||||
return (x);
|
||||
}
|
||||
)cpp",
|
||||
"const struct Foo &",
|
||||
},
|
||||
{
|
||||
R"cpp( // auto on alias
|
||||
struct Foo {};
|
||||
using Bar = Foo;
|
||||
^auto x = Bar();
|
||||
)cpp",
|
||||
// FIXME: it'd be nice if this resolved to the alias instead
|
||||
"struct Foo",
|
||||
},
|
||||
};
|
||||
for (Test T : Tests) {
|
||||
Annotations File(T.AnnotatedCode);
|
||||
auto AST = TestTU::withCode(File.code()).build();
|
||||
SourceManagerForFile SM("foo.cpp", File.code());
|
||||
|
||||
SCOPED_TRACE(File.code());
|
||||
EXPECT_FALSE(File.points().empty());
|
||||
for (Position Pos : File.points()) {
|
||||
auto Location = sourceLocationInMainFile(SM.get(), Pos);
|
||||
ASSERT_TRUE(!!Location) << llvm::toString(Location.takeError());
|
||||
auto DeducedType = getDeducedType(AST.getASTContext(), *Location);
|
||||
ASSERT_TRUE(DeducedType);
|
||||
EXPECT_EQ(DeducedType->getAsString(), T.DeducedType);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -640,6 +640,134 @@ TEST(LocateSymbol, All) {
|
|||
struct Fo^o<T*> {};
|
||||
)cpp",
|
||||
|
||||
R"cpp(// auto builtin type (not supported)
|
||||
^auto x = 42;
|
||||
)cpp",
|
||||
|
||||
R"cpp(// auto on lambda
|
||||
auto x = [[[]]]{};
|
||||
^auto y = x;
|
||||
)cpp",
|
||||
|
||||
R"cpp(// auto on struct
|
||||
namespace ns1 {
|
||||
struct [[S1]] {};
|
||||
} // namespace ns1
|
||||
|
||||
^auto x = ns1::S1{};
|
||||
)cpp",
|
||||
|
||||
R"cpp(// decltype on struct
|
||||
namespace ns1 {
|
||||
struct [[S1]] {};
|
||||
} // namespace ns1
|
||||
|
||||
ns1::S1 i;
|
||||
^decltype(i) j;
|
||||
)cpp",
|
||||
|
||||
R"cpp(// decltype(auto) on struct
|
||||
namespace ns1 {
|
||||
struct [[S1]] {};
|
||||
} // namespace ns1
|
||||
|
||||
ns1::S1 i;
|
||||
ns1::S1& j = i;
|
||||
^decltype(auto) k = j;
|
||||
)cpp",
|
||||
|
||||
R"cpp(// auto on template class
|
||||
template<typename T> class [[Foo]] {};
|
||||
|
||||
^auto x = Foo<int>();
|
||||
)cpp",
|
||||
|
||||
R"cpp(// auto on template class with forward declared class
|
||||
template<typename T> class [[Foo]] {};
|
||||
class X;
|
||||
|
||||
^auto x = Foo<X>();
|
||||
)cpp",
|
||||
|
||||
R"cpp(// auto on specialized template class
|
||||
template<typename T> class Foo {};
|
||||
template<> class [[Foo]]<int> {};
|
||||
|
||||
^auto x = Foo<int>();
|
||||
)cpp",
|
||||
|
||||
R"cpp(// auto on initializer list.
|
||||
namespace std
|
||||
{
|
||||
template<class _E>
|
||||
class [[initializer_list]] {};
|
||||
}
|
||||
|
||||
^auto i = {1,2};
|
||||
)cpp",
|
||||
|
||||
R"cpp(// auto function return with trailing type
|
||||
struct [[Bar]] {};
|
||||
^auto test() -> decltype(Bar()) {
|
||||
return Bar();
|
||||
}
|
||||
)cpp",
|
||||
|
||||
R"cpp(// decltype in trailing return type
|
||||
struct [[Bar]] {};
|
||||
auto test() -> ^decltype(Bar()) {
|
||||
return Bar();
|
||||
}
|
||||
)cpp",
|
||||
|
||||
R"cpp(// auto in function return
|
||||
struct [[Bar]] {};
|
||||
^auto test() {
|
||||
return Bar();
|
||||
}
|
||||
)cpp",
|
||||
|
||||
R"cpp(// auto& in function return
|
||||
struct [[Bar]] {};
|
||||
^auto& test() {
|
||||
static Bar x;
|
||||
return x;
|
||||
}
|
||||
)cpp",
|
||||
|
||||
R"cpp(// auto* in function return
|
||||
struct [[Bar]] {};
|
||||
^auto* test() {
|
||||
Bar* x;
|
||||
return x;
|
||||
}
|
||||
)cpp",
|
||||
|
||||
R"cpp(// const auto& in function return
|
||||
struct [[Bar]] {};
|
||||
const ^auto& test() {
|
||||
static Bar x;
|
||||
return x;
|
||||
}
|
||||
)cpp",
|
||||
|
||||
R"cpp(// decltype(auto) in function return
|
||||
struct [[Bar]] {};
|
||||
^decltype(auto) test() {
|
||||
return Bar();
|
||||
}
|
||||
)cpp",
|
||||
|
||||
R"cpp(// decltype of function with trailing return type.
|
||||
struct [[Bar]] {};
|
||||
auto test() -> decltype(Bar()) {
|
||||
return Bar();
|
||||
}
|
||||
void foo() {
|
||||
^decltype(test()) i = test();
|
||||
}
|
||||
)cpp",
|
||||
|
||||
R"cpp(// Override specifier jumps to overridden method
|
||||
class Y { virtual void $decl[[a]]() = 0; };
|
||||
class X : Y { void a() ^override {} };
|
||||
|
|
Loading…
Reference in New Issue