diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -290,6 +290,18 @@ } } + // Give the underlying decl if navigation is triggered on a non-renaming + // alias. + if (llvm::isa(D)) { + // FIXME: address more complicated cases. TargetDecl(... Underlying) gives + // all overload candidates, we only want the targeted one if the cursor is + // on an using-alias usage, workround it with getDeclAtPosition. + llvm::for_each( + getDeclAtPosition(AST, CurLoc, DeclRelation::Underlying, NodeKind), + [&](const NamedDecl *UD) { AddResultDecl(UD); }); + continue; + } + // Otherwise the target declaration is the right one. AddResultDecl(D); } diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -47,6 +47,11 @@ MATCHER_P2(FileRange, File, Range, "") { return Location{URIForFile::canonicalize(File, testRoot()), Range} == arg; } +MATCHER(DeclRange, "") { + const LocatedSymbol &Sym = ::testing::get<0>(arg); + const Range &Range = ::testing::get<1>(arg); + return Sym.PreferredDeclaration.range == Range; +} // Extracts ranges from an annotated example, and constructs a matcher for a // highlight set. Ranges should be named $read/$write as appropriate. @@ -945,15 +950,77 @@ Sym("uniqueMethodName", Header.range("BarLoc"), llvm::None))); } -TEST(LocateSymbol, TemplateTypedefs) { - auto T = Annotations(R"cpp( - template struct function {}; - template using callback = function; +TEST(LocateSymbol, Alias) { + const char *Tests[] = { + R"cpp( + template struct function {}; + template using [[callback]] = function; - c^allback foo; - )cpp"); - auto AST = TestTU::withCode(T.code()).build(); - EXPECT_THAT(locateSymbolAt(AST, T.point()), ElementsAre(Sym("callback"))); + c^allback foo; + )cpp", + + // triggered on non-definition of a renaming alias: should not give any + // underlying decls. + R"cpp( + class Foo {}; + typedef Foo [[Bar]]; + + B^ar b; + )cpp", + R"cpp( + class Foo {}; + using [[Bar]] = Foo; // definition + Ba^r b; + )cpp", + + // triggered on the underlying decl of a renaming alias. + R"cpp( + class [[Foo]]; + using Bar = Fo^o; + )cpp", + + // triggered on definition of a non-renaming alias: should give underlying + // decls. + R"cpp( + namespace ns { class [[Foo]] {}; } + using ns::F^oo; + )cpp", + + R"cpp( + namespace ns { int [[x]](char); int [[x]](double); } + using ns::^x; + )cpp", + + R"cpp( + namespace ns { int [[x]](char); int x(double); } + using ns::x; + int y = ^x('a'); + )cpp", + + R"cpp( + namespace ns { class [[Foo]] {}; } + using ns::Foo; + F^oo f; + )cpp", + + // other cases that don't matter much. + R"cpp( + class Foo {}; + typedef Foo [[Ba^r]]; + )cpp", + R"cpp( + class Foo {}; + using [[B^ar]] = Foo; + )cpp", + }; + + for (const auto* Case : Tests) { + SCOPED_TRACE(Case); + auto T = Annotations(Case); + auto AST = TestTU::withCode(T.code()).build(); + EXPECT_THAT(locateSymbolAt(AST, T.point()), + ::testing::UnorderedPointwise(DeclRange(), T.ranges())); + } } TEST(LocateSymbol, RelPathsInCompileCommand) {