[clang-tools-extra] 79a09d8 - [clangd] Show template arguments in type hierarchy when possible
Nathan Ridge via cfe-commits
cfe-commits at lists.llvm.org
Sun Jan 12 19:32:14 PST 2020
Author: Nathan Ridge
Date: 2020-01-12T22:31:40-05:00
New Revision: 79a09d8bf4d508b0ae6a1e3c90907488092678c5
URL: https://github.com/llvm/llvm-project/commit/79a09d8bf4d508b0ae6a1e3c90907488092678c5
DIFF: https://github.com/llvm/llvm-project/commit/79a09d8bf4d508b0ae6a1e3c90907488092678c5.diff
LOG: [clangd] Show template arguments in type hierarchy when possible
Summary: Fixes https://github.com/clangd/clangd/issues/31
Reviewers: kadircet
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D71533
Added:
Modified:
clang-tools-extra/clangd/XRefs.cpp
clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index fecc3668f98e..fa33c796ce29 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -672,9 +672,18 @@ const CXXRecordDecl *findRecordTypeAt(ParsedAST &AST, Position Pos) {
const SourceManager &SM = AST.getSourceManager();
SourceLocation SourceLocationBeg = SM.getMacroArgExpandedLocation(
getBeginningOfIdentifier(Pos, SM, AST.getLangOpts()));
- DeclRelationSet Relations =
- DeclRelation::TemplatePattern | DeclRelation::Underlying;
- auto Decls = getDeclAtPosition(AST, SourceLocationBeg, Relations);
+ unsigned Offset =
+ AST.getSourceManager().getDecomposedSpellingLoc(SourceLocationBeg).second;
+ SelectionTree Selection(AST.getASTContext(), AST.getTokens(), Offset);
+ const SelectionTree::Node *N = Selection.commonAncestor();
+ if (!N)
+ return nullptr;
+
+ // Note: explicitReferenceTargets() will search for both template
+ // instantiations and template patterns, and prefer the former if available
+ // (generally, one will be available for non-dependent specializations of a
+ // class template).
+ auto Decls = explicitReferenceTargets(N->ASTNode, DeclRelation::Underlying);
if (Decls.empty())
return nullptr;
@@ -700,6 +709,13 @@ const CXXRecordDecl *findRecordTypeAt(ParsedAST &AST, Position Pos) {
std::vector<const CXXRecordDecl *> typeParents(const CXXRecordDecl *CXXRD) {
std::vector<const CXXRecordDecl *> Result;
+ // If this is an invalid instantiation, instantiation of the bases
+ // may not have succeeded, so fall back to the template pattern.
+ if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD)) {
+ if (CTSD->isInvalidDecl())
+ CXXRD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
+ }
+
for (auto Base : CXXRD->bases()) {
const CXXRecordDecl *ParentDecl = nullptr;
@@ -754,6 +770,11 @@ getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels,
Result->children.emplace();
if (Index) {
+ // The index does not store relationships between implicit
+ // specializations, so if we have one, use the template pattern instead.
+ if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD))
+ CXXRD = CTSD->getTemplateInstantiationPattern();
+
if (Optional<SymbolID> ID = getSymbolID(CXXRD))
fillSubTypes(*ID, *Result->children, Index, ResolveLevels, TUPath);
}
diff --git a/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp b/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp
index 76137850b91f..f365a141e208 100644
--- a/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp
+++ b/clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp
@@ -409,17 +409,21 @@ TEST(TypeHierarchy, RecursiveHierarchyUnbounded) {
ASSERT_TRUE(!AST.getDiagnostics().empty());
// Make sure getTypeHierarchy() doesn't get into an infinite recursion.
- // FIXME(nridge): It would be preferable if the type hierarchy gave us type
- // names (e.g. "S<0>" for the child and "S<1>" for the parent) rather than
- // template names (e.g. "S").
+ // The parent is reported as "S" because "S<0>" is an invalid instantiation.
+ // We then iterate once more and find "S" again before detecting the
+ // recursion.
llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
AST, Source.points()[0], 0, TypeHierarchyDirection::Parents);
ASSERT_TRUE(bool(Result));
EXPECT_THAT(
*Result,
- AllOf(WithName("S"), WithKind(SymbolKind::Struct),
- Parents(AllOf(WithName("S"), WithKind(SymbolKind::Struct),
- SelectionRangeIs(Source.range("SDef")), Parents()))));
+ AllOf(WithName("S<0>"), WithKind(SymbolKind::Struct),
+ Parents(
+ AllOf(WithName("S"), WithKind(SymbolKind::Struct),
+ SelectionRangeIs(Source.range("SDef")),
+ Parents(AllOf(WithName("S"), WithKind(SymbolKind::Struct),
+ SelectionRangeIs(Source.range("SDef")),
+ Parents()))))));
}
TEST(TypeHierarchy, RecursiveHierarchyBounded) {
@@ -449,9 +453,12 @@ TEST(TypeHierarchy, RecursiveHierarchyBounded) {
ASSERT_TRUE(bool(Result));
EXPECT_THAT(
*Result,
- AllOf(WithName("S"), WithKind(SymbolKind::Struct),
- Parents(AllOf(WithName("S"), WithKind(SymbolKind::Struct),
- SelectionRangeIs(Source.range("SDef")), Parents()))));
+ AllOf(WithName("S<2>"), WithKind(SymbolKind::Struct),
+ Parents(AllOf(
+ WithName("S<1>"), WithKind(SymbolKind::Struct),
+ SelectionRangeIs(Source.range("SDef")),
+ Parents(AllOf(WithName("S<0>"), WithKind(SymbolKind::Struct),
+ Parents()))))));
Result = getTypeHierarchy(AST, Source.point("SRefDependent"), 0,
TypeHierarchyDirection::Parents);
ASSERT_TRUE(bool(Result));
@@ -462,6 +469,83 @@ TEST(TypeHierarchy, RecursiveHierarchyBounded) {
SelectionRangeIs(Source.range("SDef")), Parents()))));
}
+TEST(TypeHierarchy, DeriveFromImplicitSpec) {
+ Annotations Source(R"cpp(
+ template <typename T>
+ struct Parent {};
+
+ struct Child : Parent<int> {};
+
+ Parent<int> Fo^o;
+ )cpp");
+
+ TestTU TU = TestTU::withCode(Source.code());
+ auto AST = TU.build();
+ auto Index = TU.index();
+ ASSERT_TRUE(AST.getDiagnostics().empty());
+
+ llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
+ AST, Source.points()[0], 2, TypeHierarchyDirection::Children, Index.get(),
+ testPath(TU.Filename));
+ ASSERT_TRUE(bool(Result));
+ EXPECT_THAT(*Result,
+ AllOf(WithName("Parent<int>"), WithKind(SymbolKind::Struct),
+ Children(AllOf(WithName("Child"),
+ WithKind(SymbolKind::Struct), Children()))));
+}
+
+TEST(TypeHierarchy, DeriveFromPartialSpec) {
+ Annotations Source(R"cpp(
+ template <typename T> struct Parent {};
+ template <typename T> struct Parent<T*> {};
+
+ struct Child : Parent<int*> {};
+
+ Parent<int> Fo^o;
+ )cpp");
+
+ TestTU TU = TestTU::withCode(Source.code());
+ auto AST = TU.build();
+ auto Index = TU.index();
+ ASSERT_TRUE(AST.getDiagnostics().empty());
+
+ llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
+ AST, Source.points()[0], 2, TypeHierarchyDirection::Children, Index.get(),
+ testPath(TU.Filename));
+ ASSERT_TRUE(bool(Result));
+ EXPECT_THAT(*Result, AllOf(WithName("Parent<int>"),
+ WithKind(SymbolKind::Struct), Children()));
+}
+
+TEST(TypeHierarchy, DeriveFromTemplate) {
+ Annotations Source(R"cpp(
+ template <typename T>
+ struct Parent {};
+
+ template <typename T>
+ struct Child : Parent<T> {};
+
+ Parent<int> Fo^o;
+ )cpp");
+
+ TestTU TU = TestTU::withCode(Source.code());
+ auto AST = TU.build();
+ auto Index = TU.index();
+ ASSERT_TRUE(AST.getDiagnostics().empty());
+
+ // FIXME: We'd like this to return the implicit specialization Child<int>,
+ // but currently libIndex does not expose relationships between
+ // implicit specializations.
+ llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
+ AST, Source.points()[0], 2, TypeHierarchyDirection::Children, Index.get(),
+ testPath(TU.Filename));
+ ASSERT_TRUE(bool(Result));
+ EXPECT_THAT(*Result,
+ AllOf(WithName("Parent<int>"), WithKind(SymbolKind::Struct),
+ Children(AllOf(WithName("Child"),
+ WithKind(SymbolKind::Struct), Children()))));
+}
+
SymbolID findSymbolIDByName(SymbolIndex *Index, llvm::StringRef Name,
llvm::StringRef TemplateArgs = "") {
SymbolID Result;
More information about the cfe-commits
mailing list