diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp --- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp +++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp @@ -473,6 +473,18 @@ ExpectedHint{": int", "waldo"}); } +TEST(TypeHints, FunctionTemplateExplicitInstantiation) { + // We used to erroneously emit bad type hints in the template for every + // explicit specialization: https://github.com/clangd/clangd/issues/1034 + assertTypeHints(R"cpp( + template void foo() { + auto x = T{}; + } + template void foo(); + template void foo(); + )cpp"); +} + TEST(TypeHints, Decorations) { assertTypeHints(R"cpp( int x = 42; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2064,6 +2064,7 @@ TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc())); TRY_TO(TraverseDeclarationNameInfo(D->getNameInfo())); + bool BodyWasInstantiated = false; // If we're an explicit template specialization, iterate over the // template args that were explicitly specified. If we were doing // this in typing order, we'd do it between the return type and @@ -2071,8 +2072,7 @@ // above, so we have to choose one side. I've decided to do before. if (const FunctionTemplateSpecializationInfo *FTSI = D->getTemplateSpecializationInfo()) { - if (FTSI->getTemplateSpecializationKind() != TSK_Undeclared && - FTSI->getTemplateSpecializationKind() != TSK_ImplicitInstantiation) { + if (FTSI->isExplicitInstantiationOrSpecialization()) { // A specialization might not have explicit template arguments if it has // a templated return type and concrete arguments. if (const ASTTemplateArgumentListInfo *TALI = @@ -2081,6 +2081,8 @@ TALI->NumTemplateArgs)); } } + BodyWasInstantiated = + isTemplateInstantiation(FTSI->getTemplateSpecializationKind()); } // Visit the function type itself, which can be either @@ -2104,16 +2106,11 @@ TRY_TO(TraverseStmt(TrailingRequiresClause)); } - if (CXXConstructorDecl *Ctor = dyn_cast(D)) { - // Constructor initializers. - for (auto *I : Ctor->inits()) { - if (I->isWritten() || getDerived().shouldVisitImplicitCode()) - TRY_TO(TraverseConstructorInitializer(I)); - } - } - bool VisitBody = D->isThisDeclarationADefinition() && + // Don't visit the function body if it was instantiated, not written. + (!BodyWasInstantiated || + getDerived().shouldVisitTemplateInstantiations()) && // Don't visit the function body if the function definition is generated // by clang. (!D->isDefaulted() || getDerived().shouldVisitImplicitCode()); @@ -2128,6 +2125,14 @@ } if (VisitBody) { + if (CXXConstructorDecl *Ctor = dyn_cast(D)) { + // Constructor initializers. + for (auto *I : Ctor->inits()) { + if (I->isWritten() || getDerived().shouldVisitImplicitCode()) + TRY_TO(TraverseConstructorInitializer(I)); + } + } + TRY_TO(TraverseStmt(D->getBody())); // Body may contain using declarations whose shadows are parented to the // FunctionDecl itself. diff --git a/clang/lib/CodeGen/CodeGenPGO.cpp b/clang/lib/CodeGen/CodeGenPGO.cpp --- a/clang/lib/CodeGen/CodeGenPGO.cpp +++ b/clang/lib/CodeGen/CodeGenPGO.cpp @@ -154,6 +154,8 @@ struct MapRegionCounters : public RecursiveASTVisitor { using Base = RecursiveASTVisitor; + bool shouldVisitTemplateInstantiations() { return true; } + /// The next counter value to assign. unsigned NextCounter; /// The function hash. diff --git a/clang/unittests/Tooling/RecursiveASTVisitorTests/Templates.cpp b/clang/unittests/Tooling/RecursiveASTVisitorTests/Templates.cpp --- a/clang/unittests/Tooling/RecursiveASTVisitorTests/Templates.cpp +++ b/clang/unittests/Tooling/RecursiveASTVisitorTests/Templates.cpp @@ -218,11 +218,9 @@ ADD_FAILURE() << "Could not find an explicit instantiation!"; return Ctx.getTranslationUnitDecl(); }; - // FIXME: we should not visit the body, as instantiation traversals are off. checkLog("Explicitly traversing an explicit instantiation", Visitor, Code, R"( TraverseFunctionDecl VisitFunctionDecl foo ExplicitInstantiationDefinition - VisitCompoundStmt )"); }