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 @@ -20,6 +20,7 @@ #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Specifiers.h" #include "llvm/ADT/STLExtras.h" @@ -169,12 +170,38 @@ return true; } + // Report all (partial) specializations of a class/var template decl. + template + void reportSpecializations(SourceLocation Loc, NamedDecl *ND) { + if (const auto *TD = dyn_cast(ND)) { + for (auto *Spec : TD->specializations()) + report(Loc, Spec, RefType::Ambiguous); + 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 = 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,60 @@ "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 {}; // partial specialization +} + )cpp", + "using ns::^Z;"); + testWalk(R"cpp( +namespace ns { +template class $ambiguous^Z {}; // primary template +template<> class $ambiguous^Z {}; // full specialization +template<> class $ambiguous^Z {}; // full specialization +} + )cpp", + "using ns::^Z;"); + + // Var templates + testWalk(R"cpp( +namespace ns { +template +T $ambiguous^foo; // primary + +template<> +int* $ambiguous^foo; // full specialization +} + )cpp", + "using ns::^foo;"); + testWalk(R"cpp( +namespace ns { +template +T $ambiguous^bar; // primary + +template +T* $ambiguous^bar; // partial specialization +} + )cpp", + "using ns::^bar;"); + + // Function templates, no partial template specializations. + testWalk(R"cpp( +namespace ns { +template +void $ambiguous^function(T); // primary + +template<> +void $ambiguous^function(int); // full specialization +} + )cpp", + "using ns::^function;"); +} + TEST(WalkAST, Alias) { testWalk(R"cpp(