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));
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,
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);
}
}
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 {} };