[clang-tidy] Fix a false positive in unused-using-decl check

The previous matcher "hasAnyTemplateArgument(templateArgument())" only
matches the first template argument, but the check wants to iterate all
template arguments. This patch fixes this.

Also some refactorings in this patch (to make the code reusable).

git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@370760 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/clang-tidy/misc/UnusedUsingDeclsCheck.cpp b/clang-tidy/misc/UnusedUsingDeclsCheck.cpp
index 256c676..dafd32e 100644
--- a/clang-tidy/misc/UnusedUsingDeclsCheck.cpp
+++ b/clang-tidy/misc/UnusedUsingDeclsCheck.cpp
@@ -17,6 +17,29 @@
 namespace tidy {
 namespace misc {
 
+namespace {
+// FIXME: Move ASTMatcher library.
+AST_POLYMORPHIC_MATCHER_P(
+    forEachTemplateArgument,
+    AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
+                                    TemplateSpecializationType, FunctionDecl),
+    clang::ast_matchers::internal::Matcher<TemplateArgument>, InnerMatcher) {
+  ArrayRef<TemplateArgument> TemplateArgs =
+      clang::ast_matchers::internal::getTemplateSpecializationArgs(Node);
+  clang::ast_matchers::internal::BoundNodesTreeBuilder Result;
+  bool Matched = false;
+  for (const auto &Arg : TemplateArgs) {
+    clang::ast_matchers::internal::BoundNodesTreeBuilder ArgBuilder(*Builder);
+    if (InnerMatcher.matches(Arg, Finder, &ArgBuilder)) {
+      Matched = true;
+      Result.addMatch(ArgBuilder);
+    }
+  }
+  *Builder = std::move(Result);
+  return Matched;
+}
+} // 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.
@@ -37,11 +60,10 @@
   Finder->addMatcher(callExpr(callee(unresolvedLookupExpr().bind("used"))),
                      this);
   Finder->addMatcher(
-      callExpr(hasDeclaration(functionDecl(hasAnyTemplateArgument(
-          anyOf(refersToTemplate(templateName().bind("used")),
-                refersToDeclaration(functionDecl().bind("used"))))))),
+      callExpr(hasDeclaration(functionDecl(
+          forEachTemplateArgument(templateArgument().bind("used"))))),
       this);
-  Finder->addMatcher(loc(templateSpecializationType(hasAnyTemplateArgument(
+  Finder->addMatcher(loc(templateSpecializationType(forEachTemplateArgument(
                          templateArgument().bind("used")))),
                      this);
 }
@@ -80,52 +102,47 @@
       Contexts.push_back(Context);
     return;
   }
-  // Mark using declarations as used by setting FoundDecls' value to zero. As
-  // the AST is walked in order, usages are only marked after a the
-  // corresponding using declaration has been found.
-  // FIXME: This currently doesn't look at whether the type reference is
-  // actually found with the help of the using declaration.
-  if (const auto *Used = Result.Nodes.getNodeAs<NamedDecl>("used")) {
+
+  // Mark a corresponding using declaration as used.
+  auto RemoveNamedDecl = [&](const NamedDecl *Used) {
+    removeFromFoundDecls(Used);
+    // Also remove variants of Used.
     if (const auto *FD = dyn_cast<FunctionDecl>(Used)) {
       removeFromFoundDecls(FD->getPrimaryTemplate());
     } else if (const auto *Specialization =
                    dyn_cast<ClassTemplateSpecializationDecl>(Used)) {
-      Used = Specialization->getSpecializedTemplate();
+      removeFromFoundDecls(Specialization->getSpecializedTemplate());
+    } else if (const auto *FD = dyn_cast<FunctionDecl>(Used)) {
+      if (const auto *FDT = FD->getPrimaryTemplate())
+        removeFromFoundDecls(FDT);
+    } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(Used)) {
+      if (const auto *ET = ECD->getType()->getAs<EnumType>())
+        removeFromFoundDecls(ET->getDecl());
     }
-    removeFromFoundDecls(Used);
+  };
+  // We rely on the fact that the clang AST is walked in order, usages are only
+  // marked after a corresponding using decl has been found.
+  if (const auto *Used = Result.Nodes.getNodeAs<NamedDecl>("used")) {
+    RemoveNamedDecl(Used);
     return;
   }
 
   if (const auto *Used = Result.Nodes.getNodeAs<TemplateArgument>("used")) {
-    // FIXME: Support non-type template parameters.
     if (Used->getKind() == TemplateArgument::Template) {
       if (const auto *TD = Used->getAsTemplate().getAsTemplateDecl())
         removeFromFoundDecls(TD);
     } else if (Used->getKind() == TemplateArgument::Type) {
       if (auto *RD = Used->getAsType()->getAsCXXRecordDecl())
         removeFromFoundDecls(RD);
+    } else if (Used->getKind() == TemplateArgument::Declaration) {
+      RemoveNamedDecl(Used->getAsDecl());
     }
     return;
   }
 
-  if (const auto *Used = Result.Nodes.getNodeAs<TemplateName>("used")) {
-    removeFromFoundDecls(Used->getAsTemplateDecl());
-    return;
-  }
-
   if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("used")) {
-    if (const auto *FD = dyn_cast<FunctionDecl>(DRE->getDecl())) {
-      if (const auto *FDT = FD->getPrimaryTemplate())
-        removeFromFoundDecls(FDT);
-      else
-        removeFromFoundDecls(FD);
-    } else if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
-      removeFromFoundDecls(VD);
-    } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl())) {
-      removeFromFoundDecls(ECD);
-      if (const auto *ET = ECD->getType()->getAs<EnumType>())
-        removeFromFoundDecls(ET->getDecl());
-    }
+    RemoveNamedDecl(DRE->getDecl());
+    return;
   }
   // Check the uninstantiated template function usage.
   if (const auto *ULE = Result.Nodes.getNodeAs<UnresolvedLookupExpr>("used")) {
diff --git a/test/clang-tidy/misc-unused-using-decls.cpp b/test/clang-tidy/misc-unused-using-decls.cpp
index eed0bae..9511160 100644
--- a/test/clang-tidy/misc-unused-using-decls.cpp
+++ b/test/clang-tidy/misc-unused-using-decls.cpp
@@ -31,6 +31,8 @@
 template <int T> class P {};
 const int Constant = 0;
 
+template <typename T> class Q {};
+
 class Base {
  public:
   void f();
@@ -169,6 +171,8 @@
 using n::Constant;
 // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'Constant' is unused
 
+using n::Q;
+
 // ----- Usages -----
 void f(B b);
 void g() {
@@ -202,3 +206,8 @@
 template <int T>
 void i(n::P<T>* t) {}
 template void i(n::P<Constant>* t);
+
+template <typename T, template <typename> class U> class Bar {};
+// We used to report Q unsued, because we only checked the first template
+// argument.
+Bar<int, Q> *bar;