diff --git a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp --- a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp +++ b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp @@ -24,6 +24,7 @@ #include "clang/Basic/Specifiers.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/STLFunctionalExtras.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" namespace clang::include_cleaner { @@ -169,12 +170,39 @@ return true; } + // Report all (partial) specializations of a class/var template decl. + template + void reportSpecializations(SourceLocation Loc, NamedDecl *ND) { + const auto *TD = llvm::dyn_cast(ND); + if (!TD) + return; + + for (auto *Spec : TD->specializations()) + report(Loc, Spec, RefType::Ambiguous); + llvm::SmallVector PartialSpecializations; + TD->getPartialSpecializations(PartialSpecializations); + for (auto *PartialSpec : PartialSpecializations) + report(Loc, PartialSpec, RefType::Ambiguous); + } bool VisitUsingDecl(UsingDecl *UD) { for (const auto *Shadow : UD->shadows()) { auto *TD = Shadow->getTargetDecl(); auto IsUsed = TD->isUsed() || TD->isReferenced(); report(UD->getLocation(), TD, IsUsed ? RefType::Explicit : RefType::Ambiguous); + + // All (partial) template specializations are visible via a using-decl, + // However a using-decl only refers to the primary template (per C++ name + // lookup). Thus, we need to manually report all specializations. + reportSpecializations( + UD->getLocation(), TD); + reportSpecializations( + UD->getLocation(), TD); + if (const auto *FTD = llvm::dyn_cast(TD)) + for (auto *Spec : FTD->specializations()) + report(UD->getLocation(), Spec, RefType::Ambiguous); } return true; } diff --git a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp --- a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp +++ b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp @@ -252,6 +252,36 @@ "auto x = [] { ^foo(); };"), ElementsAre(Decl::FunctionTemplate)); } +TEST(WalkAST, TemplateSpecializationsFromUsingDecl) { + // Class templates + testWalk(R"cpp( +namespace ns { +template class $ambiguous^Z {}; // primary template +template class $ambiguous^Z {}; // partial specialization +template<> class $ambiguous^Z {}; // full specialization +} + )cpp", + "using ns::^Z;"); + + // Var templates + testWalk(R"cpp( +namespace ns { +template T $ambiguous^foo; // primary template +template T $ambiguous^foo; // partial specialization +template<> int* $ambiguous^foo; // full specialization +} + )cpp", + "using ns::^foo;"); + // Function templates, no partial template specializations. + testWalk(R"cpp( +namespace ns { +template void $ambiguous^function(T); // primary template +template<> void $ambiguous^function(int); // full specialization +} + )cpp", + "using ns::^function;"); +} + TEST(WalkAST, Alias) { testWalk(R"cpp(