[llvm-branch-commits] [clang-tools-extra] bda7d0a - [clangd] Improve goToDefinition on auto and dectype
Sam McCall via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Dec 15 07:42:01 PST 2020
Author: Quentin Chateau
Date: 2020-12-15T16:32:22+01:00
New Revision: bda7d0af970718c243d93b22a8449c20156e574f
URL: https://github.com/llvm/llvm-project/commit/bda7d0af970718c243d93b22a8449c20156e574f
DIFF: https://github.com/llvm/llvm-project/commit/bda7d0af970718c243d93b22a8449c20156e574f.diff
LOG: [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
Added:
Modified:
clang-tools-extra/clangd/XRefs.cpp
clang-tools-extra/clangd/unittests/ASTTests.cpp
clang-tools-extra/clangd/unittests/XRefsTests.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index ac4543026a9f..c7ec401c6479 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -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,
diff --git a/clang-tools-extra/clangd/unittests/ASTTests.cpp b/clang-tools-extra/clangd/unittests/ASTTests.cpp
index 21f70dcc3168..4c52c72d703c 100644
--- a/clang-tools-extra/clangd/unittests/ASTTests.cpp
+++ b/clang-tools-extra/clangd/unittests/ASTTests.cpp
@@ -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);
}
}
diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index dfe1f6ce6ccc..26e06dad9250 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -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 {} };
More information about the llvm-branch-commits
mailing list