diff --git a/clang-tools-extra/include-cleaner/lib/Record.cpp b/clang-tools-extra/include-cleaner/lib/Record.cpp --- a/clang-tools-extra/include-cleaner/lib/Record.cpp +++ b/clang-tools-extra/include-cleaner/lib/Record.cpp @@ -12,6 +12,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclGroup.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/MacroInfo.h" #include "clang/Lex/PPCallbacks.h" @@ -352,6 +353,15 @@ return IWYUPublic.find(FE->getUniqueID()) != IWYUPublic.end(); } +namespace { +template +bool isImplicitTemplateSpecialization(const NamedDecl *D) { + if (const auto *TD = dyn_cast(D)) + return TD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation; + return false; +} +} // namespace + std::unique_ptr RecordedAST::record() { class Recorder : public ASTConsumer { RecordedAST *Out; @@ -364,11 +374,21 @@ for (Decl *D : DG) { if (!SM.isWrittenInMainFile(SM.getExpansionLoc(D->getLocation()))) continue; - // FIXME: Filter out certain Obj-C and template-related decls. + if (const NamedDecl *ND = dyn_cast(D)) + if (isImplicitTemplateInstantiation(ND)) + continue; + // FIXME: Filter out certain Obj-C as well. Out->Roots.push_back(D); } return ASTConsumer::HandleTopLevelDecl(DG); } + + private: + bool isImplicitTemplateInstantiation(const NamedDecl *D) { + return isImplicitTemplateSpecialization(D) || + isImplicitTemplateSpecialization(D) || + isImplicitTemplateSpecialization(D); + } }; return std::make_unique(this); diff --git a/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp b/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp --- a/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp +++ b/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp @@ -101,6 +101,28 @@ EXPECT_THAT(Recorded.Roots, testing::ElementsAre(named("x"))); } +// Decl from template instantiation is filtered out from roots. +TEST_F(RecordASTTest, TemplateInstantiations) { + Inputs.ExtraFiles["dispatch.h"] = R"cpp( + struct A { + static constexpr int value = 1; + }; + template + int dispatch() { + return Getter::template get(); + } + )cpp"; + Inputs.Code = R"cpp( + #include "dispatch.h" + struct MyGetter { + template static int get() { return T::value; } + }; + int v = dispatch(); + )cpp"; + auto AST = build(); + EXPECT_THAT(Recorded.Roots, testing::Not(testing::ElementsAre(named("get")))); +} + class RecordPPTest : public ::testing::Test { protected: TestInputs Inputs;