Index: clang-tidy/misc/UnusedUsingDeclsCheck.h =================================================================== --- clang-tidy/misc/UnusedUsingDeclsCheck.h +++ clang-tidy/misc/UnusedUsingDeclsCheck.h @@ -11,6 +11,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_USING_DECLS_H #include "../ClangTidy.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallPtrSet.h" #include @@ -49,6 +50,17 @@ }; std::vector Contexts; + // Mapping from the declaration (CXXRecordDecl, EnumDecl), which is referenced + // from a template argument of an explicit template instantiation, to the + // source location of the instantiation. + // + // It is used to determine whether a declaration of using-decl is used in + // explicit template instantiations. + // As there is no AST node for explicit template instantiations, regular + // matchers like "loc" can't find types referenced in template argument + // of an explicit template instantiation. We handle this case specifically. + llvm::DenseMap + TemplateArgsInExplicitInstantiation; }; } // namespace misc Index: clang-tidy/misc/UnusedUsingDeclsCheck.cpp =================================================================== --- clang-tidy/misc/UnusedUsingDeclsCheck.cpp +++ clang-tidy/misc/UnusedUsingDeclsCheck.cpp @@ -18,6 +18,17 @@ namespace tidy { namespace misc { +namespace { +AST_POLYMORPHIC_MATCHER( + isExplicitInstantiation, + AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl, + FunctionDecl)) { + auto Kind = Node.getTemplateSpecializationKind(); + return Kind == TSK_ExplicitInstantiationDeclaration || + Kind == TSK_ExplicitInstantiationDefinition; +} +} // namespace + // A function that helps to tell whether a TargetDecl in a UsingDecl will be // checked. Only variable, function, function template, class template, class, // enum declaration and enum constant declaration are considered. @@ -45,6 +56,23 @@ Finder->addMatcher(loc(templateSpecializationType(hasAnyTemplateArgument( templateArgument().bind("used")))), this); + + auto TypeTemplateArgument = templateArgument( + anyOf(refersToType(enumType()), + refersToType(qualType(hasDeclaration(cxxRecordDecl()))))); + // Find usage in explicitly instantiated function declarations. + Finder->addMatcher(functionDecl(hasAnyTemplateArgument( + TypeTemplateArgument.bind("maybe_used")), + isExplicitInstantiation()) + .bind("fun_decl"), + this); + // Find usages in explicitly instantiated class declarations. + Finder->addMatcher( + classTemplateSpecializationDecl( + hasAnyTemplateArgument(TypeTemplateArgument.bind("maybe_used")), + isExplicitInstantiation()) + .bind("class_decl"), + this); } void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) { @@ -94,6 +122,26 @@ return; } + if (const auto *Used = + Result.Nodes.getNodeAs("maybe_used")) { + SourceLocation InstantiationLoc; + if (const auto *DC = Result.Nodes.getNodeAs("fun_decl")) { + InstantiationLoc = DC->getPointOfInstantiation(); + } else if (const auto *DC = + Result.Nodes.getNodeAs( + "class_decl")) { + InstantiationLoc= DC->getPointOfInstantiation(); + } + + assert(Used->getKind() == TemplateArgument::Type); + QualType Type = Used->getAsType(); + if (auto *RD = Type->getAsCXXRecordDecl()) { + TemplateArgsInExplicitInstantiation[RD] = InstantiationLoc; + } else if (auto *ET = Type->getAs()) { + TemplateArgsInExplicitInstantiation[ET->getDecl()] = InstantiationLoc; + } + } + if (const auto *Used = Result.Nodes.getNodeAs("used")) { // FIXME: Support non-type template parameters. if (Used->getKind() == TemplateArgument::Template) { @@ -151,9 +199,25 @@ void UnusedUsingDeclsCheck::onEndOfTranslationUnit() { for (const auto &Context : Contexts) { if (!Context.IsUsed) { - diag(Context.FoundUsingDecl->getLocation(), "using decl %0 is unused") - << Context.FoundUsingDecl - << FixItHint::CreateRemoval(Context.UsingDeclRange); + // Filter out all usages in explicit template instantiations. + bool IsUsed = false; + for (auto Iter : TemplateArgsInExplicitInstantiation) { + if (Context.UsingTargetDecls.count(Iter.first) > 0) { + const auto &SM = Iter.first->getASTContext().getSourceManager(); + // Check whether the source location of instantiation is before the + // source location of using-decl. + if (SM.isBeforeInTranslationUnit( + Context.FoundUsingDecl->getLocation(), Iter.second)) { + IsUsed = true; + break; + } + } + } + if (!IsUsed) { + diag(Context.FoundUsingDecl->getLocation(), "using decl %0 is unused") + << Context.FoundUsingDecl + << FixItHint::CreateRemoval(Context.UsingDeclRange); + } } } Contexts.clear(); Index: test/clang-tidy/misc-unused-using-decls.cpp =================================================================== --- test/clang-tidy/misc-unused-using-decls.cpp +++ test/clang-tidy/misc-unused-using-decls.cpp @@ -29,6 +29,15 @@ class N {}; template class P {}; + +class Q {}; + +template class R {}; + +class S {}; + +class U {}; +class V {}; const int Constant = 0; class Base { @@ -63,6 +72,10 @@ enum Color4 { Blue }; +enum Color5 {}; +enum Color6 {}; +enum Color7 {}; +enum Color8 {}; } // namespace n #include "unused-using-decls.h" @@ -156,6 +169,8 @@ using n::Color2; using n::Color3; using n::Blue; +using n::Color5; +using n::Color7; using ns::AA; using ns::ff; @@ -168,6 +183,10 @@ using n::Constant; // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'Constant' is unused +using n::Q; + +using n::U; + // ----- Usages ----- void f(B b); void g() { @@ -201,3 +220,30 @@ template void i(n::P* t) {} template void i(n::P* t); + +// Test template arguments in explicit function template instantiations. +template +void j() {}; +template void j(); +template void j(); + +template void j(); +using n::S; +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'S' is unused +template void j(); +using n::Color6; +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'Color6' is unused + +// Test template arguments in explicit class template instantiations. +template +class k {}; +template class k; +template class k; + +template class k; +using n::V; +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'V' is unused + +template class k; +using n::Color8; +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'Color8' is unused