Skip to content

Commit 79a09d8

Browse files
[clangd] Show template arguments in type hierarchy when possible
Summary: Fixes clangd/clangd#31 Reviewers: kadircet Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D71533
1 parent 1ad1308 commit 79a09d8

File tree

2 files changed

+117
-12
lines changed

2 files changed

+117
-12
lines changed

clang-tools-extra/clangd/XRefs.cpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -672,9 +672,18 @@ const CXXRecordDecl *findRecordTypeAt(ParsedAST &AST, Position Pos) {
672672
const SourceManager &SM = AST.getSourceManager();
673673
SourceLocation SourceLocationBeg = SM.getMacroArgExpandedLocation(
674674
getBeginningOfIdentifier(Pos, SM, AST.getLangOpts()));
675-
DeclRelationSet Relations =
676-
DeclRelation::TemplatePattern | DeclRelation::Underlying;
677-
auto Decls = getDeclAtPosition(AST, SourceLocationBeg, Relations);
675+
unsigned Offset =
676+
AST.getSourceManager().getDecomposedSpellingLoc(SourceLocationBeg).second;
677+
SelectionTree Selection(AST.getASTContext(), AST.getTokens(), Offset);
678+
const SelectionTree::Node *N = Selection.commonAncestor();
679+
if (!N)
680+
return nullptr;
681+
682+
// Note: explicitReferenceTargets() will search for both template
683+
// instantiations and template patterns, and prefer the former if available
684+
// (generally, one will be available for non-dependent specializations of a
685+
// class template).
686+
auto Decls = explicitReferenceTargets(N->ASTNode, DeclRelation::Underlying);
678687
if (Decls.empty())
679688
return nullptr;
680689

@@ -700,6 +709,13 @@ const CXXRecordDecl *findRecordTypeAt(ParsedAST &AST, Position Pos) {
700709
std::vector<const CXXRecordDecl *> typeParents(const CXXRecordDecl *CXXRD) {
701710
std::vector<const CXXRecordDecl *> Result;
702711

712+
// If this is an invalid instantiation, instantiation of the bases
713+
// may not have succeeded, so fall back to the template pattern.
714+
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD)) {
715+
if (CTSD->isInvalidDecl())
716+
CXXRD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
717+
}
718+
703719
for (auto Base : CXXRD->bases()) {
704720
const CXXRecordDecl *ParentDecl = nullptr;
705721

@@ -754,6 +770,11 @@ getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels,
754770
Result->children.emplace();
755771

756772
if (Index) {
773+
// The index does not store relationships between implicit
774+
// specializations, so if we have one, use the template pattern instead.
775+
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD))
776+
CXXRD = CTSD->getTemplateInstantiationPattern();
777+
757778
if (Optional<SymbolID> ID = getSymbolID(CXXRD))
758779
fillSubTypes(*ID, *Result->children, Index, ResolveLevels, TUPath);
759780
}

clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -409,17 +409,21 @@ TEST(TypeHierarchy, RecursiveHierarchyUnbounded) {
409409
ASSERT_TRUE(!AST.getDiagnostics().empty());
410410

411411
// Make sure getTypeHierarchy() doesn't get into an infinite recursion.
412-
// FIXME(nridge): It would be preferable if the type hierarchy gave us type
413-
// names (e.g. "S<0>" for the child and "S<1>" for the parent) rather than
414-
// template names (e.g. "S").
412+
// The parent is reported as "S" because "S<0>" is an invalid instantiation.
413+
// We then iterate once more and find "S" again before detecting the
414+
// recursion.
415415
llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
416416
AST, Source.points()[0], 0, TypeHierarchyDirection::Parents);
417417
ASSERT_TRUE(bool(Result));
418418
EXPECT_THAT(
419419
*Result,
420-
AllOf(WithName("S"), WithKind(SymbolKind::Struct),
421-
Parents(AllOf(WithName("S"), WithKind(SymbolKind::Struct),
422-
SelectionRangeIs(Source.range("SDef")), Parents()))));
420+
AllOf(WithName("S<0>"), WithKind(SymbolKind::Struct),
421+
Parents(
422+
AllOf(WithName("S"), WithKind(SymbolKind::Struct),
423+
SelectionRangeIs(Source.range("SDef")),
424+
Parents(AllOf(WithName("S"), WithKind(SymbolKind::Struct),
425+
SelectionRangeIs(Source.range("SDef")),
426+
Parents()))))));
423427
}
424428

425429
TEST(TypeHierarchy, RecursiveHierarchyBounded) {
@@ -449,9 +453,12 @@ TEST(TypeHierarchy, RecursiveHierarchyBounded) {
449453
ASSERT_TRUE(bool(Result));
450454
EXPECT_THAT(
451455
*Result,
452-
AllOf(WithName("S"), WithKind(SymbolKind::Struct),
453-
Parents(AllOf(WithName("S"), WithKind(SymbolKind::Struct),
454-
SelectionRangeIs(Source.range("SDef")), Parents()))));
456+
AllOf(WithName("S<2>"), WithKind(SymbolKind::Struct),
457+
Parents(AllOf(
458+
WithName("S<1>"), WithKind(SymbolKind::Struct),
459+
SelectionRangeIs(Source.range("SDef")),
460+
Parents(AllOf(WithName("S<0>"), WithKind(SymbolKind::Struct),
461+
Parents()))))));
455462
Result = getTypeHierarchy(AST, Source.point("SRefDependent"), 0,
456463
TypeHierarchyDirection::Parents);
457464
ASSERT_TRUE(bool(Result));
@@ -462,6 +469,83 @@ TEST(TypeHierarchy, RecursiveHierarchyBounded) {
462469
SelectionRangeIs(Source.range("SDef")), Parents()))));
463470
}
464471

472+
TEST(TypeHierarchy, DeriveFromImplicitSpec) {
473+
Annotations Source(R"cpp(
474+
template <typename T>
475+
struct Parent {};
476+
477+
struct Child : Parent<int> {};
478+
479+
Parent<int> Fo^o;
480+
)cpp");
481+
482+
TestTU TU = TestTU::withCode(Source.code());
483+
auto AST = TU.build();
484+
auto Index = TU.index();
485+
ASSERT_TRUE(AST.getDiagnostics().empty());
486+
487+
llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
488+
AST, Source.points()[0], 2, TypeHierarchyDirection::Children, Index.get(),
489+
testPath(TU.Filename));
490+
ASSERT_TRUE(bool(Result));
491+
EXPECT_THAT(*Result,
492+
AllOf(WithName("Parent<int>"), WithKind(SymbolKind::Struct),
493+
Children(AllOf(WithName("Child"),
494+
WithKind(SymbolKind::Struct), Children()))));
495+
}
496+
497+
TEST(TypeHierarchy, DeriveFromPartialSpec) {
498+
Annotations Source(R"cpp(
499+
template <typename T> struct Parent {};
500+
template <typename T> struct Parent<T*> {};
501+
502+
struct Child : Parent<int*> {};
503+
504+
Parent<int> Fo^o;
505+
)cpp");
506+
507+
TestTU TU = TestTU::withCode(Source.code());
508+
auto AST = TU.build();
509+
auto Index = TU.index();
510+
ASSERT_TRUE(AST.getDiagnostics().empty());
511+
512+
llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
513+
AST, Source.points()[0], 2, TypeHierarchyDirection::Children, Index.get(),
514+
testPath(TU.Filename));
515+
ASSERT_TRUE(bool(Result));
516+
EXPECT_THAT(*Result, AllOf(WithName("Parent<int>"),
517+
WithKind(SymbolKind::Struct), Children()));
518+
}
519+
520+
TEST(TypeHierarchy, DeriveFromTemplate) {
521+
Annotations Source(R"cpp(
522+
template <typename T>
523+
struct Parent {};
524+
525+
template <typename T>
526+
struct Child : Parent<T> {};
527+
528+
Parent<int> Fo^o;
529+
)cpp");
530+
531+
TestTU TU = TestTU::withCode(Source.code());
532+
auto AST = TU.build();
533+
auto Index = TU.index();
534+
ASSERT_TRUE(AST.getDiagnostics().empty());
535+
536+
// FIXME: We'd like this to return the implicit specialization Child<int>,
537+
// but currently libIndex does not expose relationships between
538+
// implicit specializations.
539+
llvm::Optional<TypeHierarchyItem> Result = getTypeHierarchy(
540+
AST, Source.points()[0], 2, TypeHierarchyDirection::Children, Index.get(),
541+
testPath(TU.Filename));
542+
ASSERT_TRUE(bool(Result));
543+
EXPECT_THAT(*Result,
544+
AllOf(WithName("Parent<int>"), WithKind(SymbolKind::Struct),
545+
Children(AllOf(WithName("Child"),
546+
WithKind(SymbolKind::Struct), Children()))));
547+
}
548+
465549
SymbolID findSymbolIDByName(SymbolIndex *Index, llvm::StringRef Name,
466550
llvm::StringRef TemplateArgs = "") {
467551
SymbolID Result;

0 commit comments

Comments
 (0)