Index: clang-tools-extra/trunk/clangd/AST.h =================================================================== --- clang-tools-extra/trunk/clangd/AST.h +++ clang-tools-extra/trunk/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 Index: clang-tools-extra/trunk/clangd/AST.cpp =================================================================== --- clang-tools-extra/trunk/clangd/AST.cpp +++ clang-tools-extra/trunk/clangd/AST.cpp @@ -15,6 +15,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" @@ -41,13 +42,57 @@ // 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); +} + bool isImplementationDetail(const Decl *D) { return !isSpelledInSource(D->getLocation(), D->getASTContext().getSourceManager()); } +// 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 +// be "" +// * symbols controlled and defined by a compile command-line option +// `-DName=foo`, the spelling location will be "". +bool isSpelledInSourceCode(const Decl *D) { + const auto &SM = D->getASTContext().getSourceManager(); + auto Loc = D->getLocation(); + // FIXME: Revisit the strategy, the heuristic is limitted when handling + // macros, we should use the location where the whole definition occurs. + if (Loc.isMacroID()) { + std::string PrintLoc = SM.getSpellingLoc(Loc).printToString(SM); + if (llvm::StringRef(PrintLoc).startswith("")) + return false; + } + return true; +} + SourceLocation findName(const clang::Decl *D) { return D->getLocation(); } Index: clang-tools-extra/trunk/clangd/ClangdUnit.cpp =================================================================== --- clang-tools-extra/trunk/clangd/ClangdUnit.cpp +++ clang-tools-extra/trunk/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,6 +20,7 @@ #include "index/CanonicalIncludes.h" #include "index/Index.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TokenKinds.h" @@ -70,6 +72,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)) Index: clang-tools-extra/trunk/clangd/CodeComplete.cpp =================================================================== --- clang-tools-extra/trunk/clangd/CodeComplete.cpp +++ clang-tools-extra/trunk/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)) Index: clang-tools-extra/trunk/clangd/unittests/ClangdUnitTests.cpp =================================================================== --- clang-tools-extra/trunk/clangd/unittests/ClangdUnitTests.cpp +++ clang-tools-extra/trunk/clangd/unittests/ClangdUnitTests.cpp @@ -6,13 +6,16 @@ // //===----------------------------------------------------------------------===// +#include "AST.h" #include "Annotations.h" #include "ClangdUnit.h" #include "SourceCode.h" #include "TestTU.h" +#include "clang/AST/DeclTemplate.h" #include "clang/Basic/TokenKinds.h" #include "clang/Tooling/Syntax/Tokens.h" #include "llvm/Support/ScopedPrinter.h" +#include "gmock/gmock-matchers.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -21,6 +24,8 @@ namespace { using ::testing::ElementsAre; +using ::testing::ElementsAreArray; +using ::testing::AllOf; TEST(ClangdUnitTest, GetBeginningOfIdentifier) { std::string Preamble = R"cpp( @@ -72,6 +77,31 @@ return false; } +// Matches if the Decl has template args equal to ArgName. If the decl is a +// NamedDecl and ArgName is an empty string it also matches. +MATCHER_P(WithTemplateArgs, ArgName, "") { + if (const FunctionDecl *FD = dyn_cast(arg)) { + if (const auto *Args = FD->getTemplateSpecializationArgs()) { + std::string SpecializationArgs; + // Without the PrintingPolicy "bool" will be printed as "_Bool". + LangOptions LO; + PrintingPolicy Policy(LO); + Policy.adjustForCPlusPlus(); + for (const auto Arg : Args->asArray()) { + if (SpecializationArgs.size() > 0) + SpecializationArgs += ","; + SpecializationArgs += Arg.getAsType().getAsString(Policy); + } + if (Args->size() == 0) + return ArgName == SpecializationArgs; + return ArgName == "<" + SpecializationArgs + ">"; + } + } + if (const NamedDecl *ND = dyn_cast(arg)) + return printTemplateSpecializationArgs(*ND) == ArgName; + return false; +} + TEST(ClangdUnitTest, TopLevelDecls) { TestTU TU; TU.HeaderCode = R"( @@ -103,6 +133,65 @@ 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 <> + struct V {}; + + template + T foo = T(10); + int i = foo; + double d = foo; + + template + int foo = 0; + template <> + int foo = 0; + )cpp"; + + auto AST = TU.build(); + EXPECT_THAT( + AST.getLocalTopLevelDecls(), + ElementsAreArray({AllOf(DeclNamed("f"), WithTemplateArgs("")), + AllOf(DeclNamed("f"), WithTemplateArgs("")), + AllOf(DeclNamed("f"), WithTemplateArgs("")), + AllOf(DeclNamed("V"), WithTemplateArgs("")), + AllOf(DeclNamed("V"), WithTemplateArgs("")), + AllOf(DeclNamed("V"), WithTemplateArgs("")), + AllOf(DeclNamed("foo"), WithTemplateArgs("")), + AllOf(DeclNamed("i"), WithTemplateArgs("")), + AllOf(DeclNamed("d"), WithTemplateArgs("")), + AllOf(DeclNamed("foo"), WithTemplateArgs("<>")), + AllOf(DeclNamed("foo"), WithTemplateArgs(""))})); +} + TEST(ClangdUnitTest, TokensAfterPreamble) { TestTU TU; TU.AdditionalFiles["foo.h"] = R"( Index: clang-tools-extra/trunk/clangd/unittests/SemanticHighlightingTests.cpp =================================================================== --- clang-tools-extra/trunk/clangd/unittests/SemanticHighlightingTests.cpp +++ clang-tools-extra/trunk/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);