diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -183,15 +183,24 @@ return D->getAsFunction(); } +// Returns the explicit specialization for implicit instantiations, otherwise +// returns D. +const NamedDecl *getExplicitSpec(const NamedDecl *D) { + if (auto *CTSD = llvm::dyn_cast(D)) { + if (!CTSD->isExplicitInstantiationOrSpecialization()) + return CTSD->getSpecializedTemplate(); + } + return D; +} + // Look up information about D from the index, and add it to Hover. -void enhanceFromIndex(HoverInfo &Hover, const Decl *D, +void enhanceFromIndex(HoverInfo &Hover, const NamedDecl &ND, const SymbolIndex *Index) { - if (!Index || !llvm::isa(D)) - return; - const NamedDecl &ND = *cast(D); - // We only add documentation, so don't bother if we already have some. - if (!Hover.Documentation.empty()) + // We only add documentation, so don't bother if we already have some, or + // Index isn't supplied. + if (!Hover.Documentation.empty() || !Index) return; + // Skip querying for non-indexable symbols, there's no point. // We're searching for symbols that might be indexed outside this main file. if (!SymbolCollector::shouldCollectSymbol(ND, ND.getASTContext(), @@ -307,8 +316,10 @@ PrintingPolicy Policy = printingPolicyForDecls(Ctx.getPrintingPolicy()); if (const NamedDecl *ND = llvm::dyn_cast(D)) { - HI.Documentation = getDeclComment(Ctx, *ND); HI.Name = printName(Ctx, *ND); + ND = getExplicitSpec(ND); + HI.Documentation = getDeclComment(Ctx, *ND); + enhanceFromIndex(HI, *ND, Index); } HI.Kind = index::getSymbolInfo(D).Kind; @@ -346,7 +357,6 @@ } HI.Definition = printDefinition(D); - enhanceFromIndex(HI, D, Index); return HI; } @@ -358,10 +368,13 @@ D = T->getAsTagDecl(); if (D) { - if (const auto *ND = llvm::dyn_cast(D)) - HI.Name = printName(ASTCtx, *ND); HI.Kind = index::getSymbolInfo(D).Kind; - enhanceFromIndex(HI, D, Index); + if (const auto *ND = llvm::dyn_cast(D)) { + HI.Name = printName(ASTCtx, *ND); + ND = getExplicitSpec(ND); + HI.Documentation = getDeclComment(ASTCtx, *ND); + enhanceFromIndex(HI, *ND, Index); + } } if (HI.Name.empty()) { diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "AST.h" #include "Annotations.h" #include "Hover.h" #include "TestIndex.h" @@ -1271,6 +1272,7 @@ [](HoverInfo &HI) { HI.Name = "Bar"; HI.Kind = index::SymbolKind::Struct; + HI.Documentation = "auto function return with trailing type"; }}, { R"cpp(// trailing return type @@ -1282,6 +1284,7 @@ [](HoverInfo &HI) { HI.Name = "Bar"; HI.Kind = index::SymbolKind::Struct; + HI.Documentation = "trailing return type"; }}, { R"cpp(// auto in function return @@ -1293,6 +1296,7 @@ [](HoverInfo &HI) { HI.Name = "Bar"; HI.Kind = index::SymbolKind::Struct; + HI.Documentation = "auto in function return"; }}, { R"cpp(// auto& in function return @@ -1305,6 +1309,7 @@ [](HoverInfo &HI) { HI.Name = "Bar"; HI.Kind = index::SymbolKind::Struct; + HI.Documentation = "auto& in function return"; }}, { R"cpp(// auto* in function return @@ -1317,6 +1322,7 @@ [](HoverInfo &HI) { HI.Name = "Bar"; HI.Kind = index::SymbolKind::Struct; + HI.Documentation = "auto* in function return"; }}, { R"cpp(// const auto& in function return @@ -1329,6 +1335,7 @@ [](HoverInfo &HI) { HI.Name = "Bar"; HI.Kind = index::SymbolKind::Struct; + HI.Documentation = "const auto& in function return"; }}, { R"cpp(// decltype(auto) in function return @@ -1340,6 +1347,7 @@ [](HoverInfo &HI) { HI.Name = "Bar"; HI.Kind = index::SymbolKind::Struct; + HI.Documentation = "decltype(auto) in function return"; }}, { R"cpp(// decltype(auto) reference in function return @@ -1404,6 +1412,8 @@ [](HoverInfo &HI) { HI.Name = "Bar"; HI.Kind = index::SymbolKind::Struct; + HI.Documentation = + "decltype of function with trailing return type."; }}, { R"cpp(// decltype of var with decltype. @@ -1477,6 +1487,87 @@ } } +TEST(Hover, DocsFromIndex) { + Annotations T(R"cpp( + template class X {}; + void foo() { + au^to t = X(); + X^ w; + (void)w; + })cpp"); + + TestTU TU = TestTU::withCode(T.code()); + auto AST = TU.build(); + for (const auto &D : AST.getDiagnostics()) + ADD_FAILURE() << D; + ASSERT_TRUE(AST.getDiagnostics().empty()); + + Symbol IndexSym; + IndexSym.ID = *getSymbolID(&findDecl(AST, "X")); + IndexSym.Documentation = "comment from index"; + SymbolSlab::Builder Symbols; + Symbols.insert(IndexSym); + auto Index = + MemIndex::build(std::move(Symbols).build(), RefSlab(), RelationSlab()); + + for (const auto &P : T.points()) { + auto H = getHover(AST, P, format::getLLVMStyle(), Index.get()); + ASSERT_TRUE(H); + EXPECT_EQ(H->Documentation, IndexSym.Documentation); + } +} + +TEST(Hover, DocsFromAST) { + Annotations T(R"cpp( + // doc + template class X {}; + void foo() { + au^to t = X(); + X^ w; + (void)w; + })cpp"); + + TestTU TU = TestTU::withCode(T.code()); + auto AST = TU.build(); + for (const auto &D : AST.getDiagnostics()) + ADD_FAILURE() << D; + ASSERT_TRUE(AST.getDiagnostics().empty()); + + for (const auto &P : T.points()) { + auto H = getHover(AST, P, format::getLLVMStyle(), nullptr); + ASSERT_TRUE(H); + EXPECT_EQ(H->Documentation, "doc"); + } +} + +TEST(Hover, DocsFromMostSpecial) { + Annotations T(R"cpp( + // doc1 + template class X {}; + // doc2 + template <> class X {}; + // doc3 + template class X {}; + void foo() { + X$doc1^(); + X$doc2^(); + X$doc3^(); + })cpp"); + + TestTU TU = TestTU::withCode(T.code()); + auto AST = TU.build(); + for (const auto &D : AST.getDiagnostics()) + ADD_FAILURE() << D; + ASSERT_TRUE(AST.getDiagnostics().empty()); + + for (auto Comment : {"doc1", "doc2", "doc3"}) { + for (const auto &P : T.points(Comment)) { + auto H = getHover(AST, P, format::getLLVMStyle(), nullptr); + ASSERT_TRUE(H); + EXPECT_EQ(H->Documentation, Comment); + } + } +} } // namespace } // namespace clangd } // namespace clang