Author: Haojian Wu Date: 2023-06-07T10:52:36+02:00 New Revision: 100ffbf991e78bb3e94a9d5c8d656a9b8648082d
URL: https://github.com/llvm/llvm-project/commit/100ffbf991e78bb3e94a9d5c8d656a9b8648082d DIFF: https://github.com/llvm/llvm-project/commit/100ffbf991e78bb3e94a9d5c8d656a9b8648082d.diff LOG: [include-cleaner] Report all specializations if the primary template is introduced by a using-decl. This will fix unused-include false positive. ``` // primary.h namespace ns { template<class T1, class T2> class Z {}; // primary template } // partial.h namespace ns { template<class T> class Z<T, T*> {}; // partial specialization } // main.cpp using ns::Z; // refs to the primary void k() { Z<int, int*> z; // use the partial specialization } ``` Differential Revision: https://reviews.llvm.org/D152345 Added: Modified: clang-tools-extra/include-cleaner/lib/WalkAST.cpp clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp Removed: ################################################################################ diff --git a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp index c593192c50191..fc392fec36865 100644 --- 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 @@ class ASTWalker : public RecursiveASTVisitor<ASTWalker> { return true; } + // Report all (partial) specializations of a class/var template decl. + template <typename TemplateDeclType, typename ParitialDeclType> + void reportSpecializations(SourceLocation Loc, NamedDecl *ND) { + const auto *TD = llvm::dyn_cast<TemplateDeclType>(ND); + if (!TD) + return; + + for (auto *Spec : TD->specializations()) + report(Loc, Spec, RefType::Ambiguous); + llvm::SmallVector<ParitialDeclType *> 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<ClassTemplateDecl, + ClassTemplatePartialSpecializationDecl>( + UD->getLocation(), TD); + reportSpecializations<VarTemplateDecl, + VarTemplatePartialSpecializationDecl>( + UD->getLocation(), TD); + if (const auto *FTD = llvm::dyn_cast<FunctionTemplateDecl>(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 index 0d504c3303f9e..525f645ec91ef 100644 --- a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp +++ b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp @@ -252,6 +252,36 @@ TEST(WalkAST, FunctionTemplates) { "auto x = [] { ^foo<int>(); };"), ElementsAre(Decl::FunctionTemplate)); } +TEST(WalkAST, TemplateSpecializationsFromUsingDecl) { + // Class templates + testWalk(R"cpp( +namespace ns { +template<class T> class $ambiguous^Z {}; // primary template +template<class T> class $ambiguous^Z<T*> {}; // partial specialization +template<> class $ambiguous^Z<int> {}; // full specialization +} + )cpp", + "using ns::^Z;"); + + // Var templates + testWalk(R"cpp( +namespace ns { +template<class T> T $ambiguous^foo; // primary template +template<class T> T $ambiguous^foo<T*>; // partial specialization +template<> int* $ambiguous^foo<int>; // full specialization +} + )cpp", + "using ns::^foo;"); + // Function templates, no partial template specializations. + testWalk(R"cpp( +namespace ns { +template<class T> void $ambiguous^function(T); // primary template +template<> void $ambiguous^function(int); // full specialization +} + )cpp", + "using ns::^function;"); +} + TEST(WalkAST, Alias) { testWalk(R"cpp( _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits