[clangd] Include the underlying decls in go-to-definition.

Fixes https://github.com/clangd/clangd/issues/277

Reviewed By: sammccall

Differential Revision: https://reviews.llvm.org/D74054
This commit is contained in:
Haojian Wu 2020-08-07 11:36:33 +02:00
parent 01bc708126
commit a70161808b
2 changed files with 87 additions and 8 deletions

View File

@ -290,6 +290,18 @@ locateASTReferent(SourceLocation CurLoc, const syntax::Token *TouchedIdentifier,
}
}
// Give the underlying decl if navigation is triggered on a non-renaming
// alias.
if (llvm::isa<UsingDecl>(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);
}

View File

@ -47,6 +47,11 @@ using ::testing::UnorderedElementsAreArray;
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 @@ TEST(LocateSymbol, TextualDependent) {
Sym("uniqueMethodName", Header.range("BarLoc"), llvm::None)));
}
TEST(LocateSymbol, TemplateTypedefs) {
auto T = Annotations(R"cpp(
template <class T> struct function {};
template <class T> using callback = function<T()>;
TEST(LocateSymbol, Alias) {
const char *Tests[] = {
R"cpp(
template <class T> struct function {};
template <class T> using [[callback]] = function<T()>;
c^allback<int> foo;
)cpp");
auto AST = TestTU::withCode(T.code()).build();
EXPECT_THAT(locateSymbolAt(AST, T.point()), ElementsAre(Sym("callback")));
c^allback<int> 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) {