Matchers related to DeclStmt for matching the count of declarations, a particular declaration within the statement, and single-Decl DeclStmts.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@162027 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h
index af1689d..33ef3dc 100644
--- a/include/clang/ASTMatchers/ASTMatchers.h
+++ b/include/clang/ASTMatchers/ASTMatchers.h
@@ -50,6 +50,7 @@
 #include "clang/ASTMatchers/ASTMatchersMacros.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/Regex.h"
+#include <iterator>
 
 namespace clang {
 namespace ast_matchers {
@@ -1275,6 +1276,21 @@
   return false;
 }
 
+/// \brief Matches the Decl of a DeclStmt which has a single declaration.
+///
+/// Given
+///   int a, b;
+///   int c;
+/// declarationStatement(hasSingleDecl(anything()))
+///   matches 'int c;' but not 'int a, b;'.
+AST_MATCHER_P(DeclStmt, hasSingleDecl, internal::Matcher<Decl>, InnerMatcher) {
+  if (Node.isSingleDecl()) {
+    const Decl *FoundDecl = Node.getSingleDecl();
+    return InnerMatcher.matches(*FoundDecl, Finder, Builder);
+  }
+  return false;
+}
+
 /// \brief Matches a variable declaration that has an initializer expression
 /// that matches the given matcher.
 ///
@@ -1320,6 +1336,44 @@
               *Node.getArg(N)->IgnoreParenImpCasts(), Finder, Builder));
 }
 
+/// \brief Matches declaration statements that contain a specific number of
+/// declarations.
+///
+/// Example: Given
+///   int a, b;
+///   int c;
+///   int d = 2, e;
+/// declCountIs(2)
+///   matches 'int a, b;' and 'int d = 2, e;', but not 'int c;'.
+AST_MATCHER_P(DeclStmt, declCountIs, unsigned, N) {
+  return std::distance(Node.decl_begin(), Node.decl_end()) == N;
+}
+
+/// \brief Matches the n'th declaration of a declaration statement.
+///
+/// Note that this does not work for global declarations because the AST
+/// breaks up multiple-declaration DeclStmt's into multiple single-declaration
+/// DeclStmt's.
+/// Example: Given non-global declarations
+///   int a, b = 0;
+///   int c;
+///   int d = 2, e;
+/// declarationStatement(containsDeclaration(
+///       0, variable(hasInitializer(anything()))))
+///   matches only 'int d = 2, e;', and
+/// declarationStatement(containsDeclaration(1, variable()))
+///   matches 'int a, b = 0' as well as 'int d = 2, e;'
+///   but 'int c;' is not matched.
+AST_MATCHER_P2(DeclStmt, containsDeclaration, unsigned, N,
+               internal::Matcher<Decl>, InnerMatcher) {
+  const unsigned NumDecls = std::distance(Node.decl_begin(), Node.decl_end());
+  if (N >= NumDecls)
+    return false;
+  DeclStmt::const_decl_iterator Iterator = Node.decl_begin();
+  std::advance(Iterator, N);
+  return InnerMatcher.matches(**Iterator, Finder, Builder);
+}
+
 /// \brief Matches a constructor initializer.
 ///
 /// Given
diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp
index bf80ad4..adf0e94 100644
--- a/unittests/ASTMatchers/ASTMatchersTest.cpp
+++ b/unittests/ASTMatchers/ASTMatchersTest.cpp
@@ -2377,6 +2377,40 @@
       declarationReference(throughUsingDecl(anything()))));
 }
 
+TEST(SingleDecl, IsSingleDecl) {
+  StatementMatcher SingleDeclStmt =
+      declarationStatement(hasSingleDecl(variable(hasInitializer(anything()))));
+  EXPECT_TRUE(matches("void f() {int a = 4;}", SingleDeclStmt));
+  EXPECT_TRUE(notMatches("void f() {int a;}", SingleDeclStmt));
+  EXPECT_TRUE(notMatches("void f() {int a = 4, b = 3;}",
+                          SingleDeclStmt));
+}
+
+TEST(DeclStmt, ContainsDeclaration) {
+  DeclarationMatcher MatchesInit = variable(hasInitializer(anything()));
+
+  EXPECT_TRUE(matches("void f() {int a = 4;}",
+                      declarationStatement(containsDeclaration(0,
+                                                               MatchesInit))));
+  EXPECT_TRUE(matches("void f() {int a = 4, b = 3;}",
+                      declarationStatement(containsDeclaration(0, MatchesInit),
+                                           containsDeclaration(1,
+                                                               MatchesInit))));
+  unsigned WrongIndex = 42;
+  EXPECT_TRUE(notMatches("void f() {int a = 4, b = 3;}",
+                         declarationStatement(containsDeclaration(WrongIndex,
+                                                      MatchesInit))));
+}
+
+TEST(DeclCount, DeclCountIsCorrect) {
+  EXPECT_TRUE(matches("void f() {int i,j;}",
+                      declarationStatement(declCountIs(2))));
+  EXPECT_TRUE(notMatches("void f() {int i,j; int k;}",
+                         declarationStatement(declCountIs(3))));
+  EXPECT_TRUE(notMatches("void f() {int i,j, k, l;}",
+                         declarationStatement(declCountIs(3))));
+}
+
 TEST(While, MatchesWhileLoops) {
   EXPECT_TRUE(notMatches("void x() {}", whileStmt()));
   EXPECT_TRUE(matches("void x() { while(true); }", whileStmt()));