diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h --- a/clang-tools-extra/clangd/AST.h +++ b/clang-tools-extra/clangd/AST.h @@ -80,9 +80,23 @@ /// take in to account using directives etc /// Example: shortenNamespace("ns1::MyClass", "ns1") /// --> "MyClass" -std::string shortenNamespace(const llvm::StringRef OriginalName, - const llvm::StringRef CurrentNamespace); - +std::string shortenNamespace(const llvm::StringRef OriginalName, + const llvm::StringRef CurrentNamespace); + +/// Indicates if \p D is a template instantiation implicitly generated by the +/// compiler, e.g. +/// template struct vector {}; +/// vector v; // 'vector' is an implicit instantiation +bool isImplicitTemplateInstantiation(const NamedDecl *D); +/// Indicates if \p D is an explicit template specialization, e.g. +/// template struct vector {}; +/// template <> struct vector {}; // <-- explicit specialization +/// +/// Note that explicit instantiations are NOT explicit specializations, albeit +/// they look similar. +/// template struct vector; // <-- explicit instantiation, NOT an +/// explicit specialization. +bool isExplicitTemplateSpecialization(const NamedDecl *D); } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp --- a/clang-tools-extra/clangd/AST.cpp +++ b/clang-tools-extra/clangd/AST.cpp @@ -14,6 +14,7 @@ #include "clang/AST/TemplateBase.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" #include "clang/Index/USRGeneration.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/Casting.h" @@ -40,8 +41,32 @@ // contain TemplateArgumentLoc information. return llvm::None; } + +template +bool isTemplateSpecializationKind(const NamedDecl *D, + TemplateSpecializationKind Kind) { + if (const auto *TD = dyn_cast(D)) + return TD->getTemplateSpecializationKind() == Kind; + return false; +} + +bool isTemplateSpecializationKind(const NamedDecl *D, + TemplateSpecializationKind Kind) { + return isTemplateSpecializationKind(D, Kind) || + isTemplateSpecializationKind(D, Kind) || + isTemplateSpecializationKind(D, Kind); +} + } // namespace +bool isImplicitTemplateInstantiation(const NamedDecl *D) { + return isTemplateSpecializationKind(D, TSK_ImplicitInstantiation); +} + +bool isExplicitTemplateSpecialization(const NamedDecl *D) { + return isTemplateSpecializationKind(D, TSK_ExplicitSpecialization); +} + // Returns true if the complete name of decl \p D is spelled in the source code. // This is not the case for: // * symbols formed via macro concatenation, the spelling location will diff --git a/clang-tools-extra/clangd/ClangdUnit.cpp b/clang-tools-extra/clangd/ClangdUnit.cpp --- a/clang-tools-extra/clangd/ClangdUnit.cpp +++ b/clang-tools-extra/clangd/ClangdUnit.cpp @@ -9,6 +9,7 @@ #include "ClangdUnit.h" #include "../clang-tidy/ClangTidyDiagnosticConsumer.h" #include "../clang-tidy/ClangTidyModuleRegistry.h" +#include "AST.h" #include "Compiler.h" #include "Diagnostics.h" #include "Headers.h" @@ -19,8 +20,11 @@ #include "index/CanonicalIncludes.h" #include "index/Index.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" #include "clang/Basic/TokenKinds.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" @@ -70,6 +74,9 @@ auto &SM = D->getASTContext().getSourceManager(); if (!isInsideMainFile(D->getLocation(), SM)) continue; + if (const NamedDecl *ND = dyn_cast(D)) + if (isImplicitTemplateInstantiation(ND)) + continue; // ObjCMethodDecl are not actually top-level decls. if (isa(D)) diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -1674,13 +1674,6 @@ } }; -template bool isExplicitTemplateSpecialization(const NamedDecl &ND) { - if (const auto *TD = dyn_cast(&ND)) - if (TD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) - return true; - return false; -} - } // namespace clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const { @@ -1783,9 +1776,7 @@ }; // We only complete symbol's name, which is the same as the name of the // *primary* template in case of template specializations. - if (isExplicitTemplateSpecialization(ND) || - isExplicitTemplateSpecialization(ND) || - isExplicitTemplateSpecialization(ND)) + if (isExplicitTemplateSpecialization(&ND)) return false; if (InTopLevelScope(ND)) diff --git a/clang-tools-extra/clangd/unittests/ClangdUnitTests.cpp b/clang-tools-extra/clangd/unittests/ClangdUnitTests.cpp --- a/clang-tools-extra/clangd/unittests/ClangdUnitTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdUnitTests.cpp @@ -103,6 +103,49 @@ EXPECT_THAT(AST.getLocalTopLevelDecls(), ElementsAre(DeclNamed("main"))); } +TEST(ClangdUnitTest, DoesNotGetImplicitTemplateTopDecls) { + TestTU TU; + TU.Code = R"cpp( + template + void f(T) {} + void s() { + f(10UL); + } + )cpp"; + + auto AST = TU.build(); + EXPECT_THAT(AST.getLocalTopLevelDecls(), + ElementsAre(DeclNamed("f"), DeclNamed("s"))); +} + +TEST(ClangdUnitTest, + GetsExplicitInstantiationAndSpecializationTemplateTopDecls) { + TestTU TU; + TU.Code = R"cpp( + template + void f(T) {} + template<> + void f(bool); + template void f(double); + + template + struct V {}; + template + struct V {}; + + template + T foo = T(10); + int i = foo; + double d = foo; + )cpp"; + + auto AST = TU.build(); + EXPECT_THAT(AST.getLocalTopLevelDecls(), + ElementsAre(DeclNamed("f"), DeclNamed("f"), DeclNamed("f"), + DeclNamed("V"), DeclNamed("V"), DeclNamed("foo"), + DeclNamed("i"), DeclNamed("d"))); +} + TEST(ClangdUnitTest, TokensAfterPreamble) { TestTU TU; TU.AdditionalFiles["foo.h"] = R"( diff --git a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp --- a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp +++ b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp @@ -249,6 +249,12 @@ template void $Function[[foo]]($TemplateParameter[[T]] ...); + )cpp", + R"cpp( + template + struct $Class[[Tmpl]] {$TemplateParameter[[T]] $Field[[x]] = 0;}; + extern template struct $Class[[Tmpl]]; + template struct $Class[[Tmpl]]; )cpp"}; for (const auto &TestCase : TestCases) { checkHighlightings(TestCase);