From ee3af39c91a92f5a03244c4034611536ad678247 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Wed, 5 Aug 2015 12:11:30 +0000 Subject: [PATCH] Add AST matchers for narrowing constructors that are default, copy, or move constructors, as well as functionality to determine whether a ctor initializer is a base initializer. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@244036 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LibASTMatchersReference.html | 52 ++++++++++++++++++ include/clang/ASTMatchers/ASTMatchers.h | 65 +++++++++++++++++++++++ lib/ASTMatchers/Dynamic/Registry.cpp | 4 ++ unittests/ASTMatchers/ASTMatchersTest.cpp | 41 ++++++++++++++ 4 files changed, 162 insertions(+) diff --git a/docs/LibASTMatchersReference.html b/docs/LibASTMatchersReference.html index c977872f38..4d383817b3 100644 --- a/docs/LibASTMatchersReference.html +++ b/docs/LibASTMatchersReference.html @@ -1460,6 +1460,58 @@ Example matches f(0, 0) (matcher = callExpr(argumentCountIs(2))) +Matcher<CXXConstructorDecl>isCopyConstructor +
Matches constructor declarations that are copy constructors.
+
+Example matches #2, but not #1 or #3 (matcher = constructorDecl(isCopyConstructor())
+  struct S {
+    S(); // #1
+    S(const S &); // #2
+    S(S &&); // #3
+  };
+
+ +Matcher<CXXConstructorDecl>isDefaultConstructor +
Matches constructor declarations that are default constructors.
+
+Example matches #1, but not #2 or #3 (matcher = constructorDecl(isDefaultConstructor())
+  struct S {
+    S(); // #1
+    S(const S &); // #2
+    S(S &&); // #3
+  };
+
+ + +Matcher<CXXConstructorDecl>isMoveConstructor +
Matches constructor declarations that are move constructors.
+
+Example matches #3, but not #1 or #2 (matcher = constructorDecl(isMoveConstructor())
+  struct S {
+    S(); // #1
+    S(const S &); // #2
+    S(S &&); // #3
+  };
+
+ + +Matcher<CXXCtorInitializer>isBaseInitializer +
Matches a constructor initializer if it is initializing a base, as opposed to a member.
+
+Given
+  struct B {};
+  struct D : B {
+    int I;
+    D(int i) : I(i) {}
+  };
+  struct E : B {
+    E() : B() {}
+  };
+constructorDecl(hasAnyConstructorInitializer(isBaseInitializer()))
+  will match E(), but not match D(int).
+
+ + Matcher<CXXCtorInitializer>isWritten
Matches a constructor initializer if it is explicitly written in
 code (as opposed to implicitly added by the compiler).
diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h
index cdd56fad4c..0e58683970 100644
--- a/include/clang/ASTMatchers/ASTMatchers.h
+++ b/include/clang/ASTMatchers/ASTMatchers.h
@@ -2632,6 +2632,26 @@ AST_MATCHER(CXXCtorInitializer, isWritten) {
   return Node.isWritten();
 }
 
+/// \brief Matches a constructor initializer if it is initializing a base, as
+/// opposed to a member.
+///
+/// Given
+/// \code
+///   struct B {};
+///   struct D : B {
+///     int I;
+///     D(int i) : I(i) {}
+///   };
+///   struct E : B {
+///     E() : B() {}
+///   };
+/// \endcode
+/// constructorDecl(hasAnyConstructorInitializer(isBaseInitializer()))
+///   will match E(), but not match D(int).
+AST_MATCHER(CXXCtorInitializer, isBaseInitializer) {
+  return Node.isBaseInitializer();
+}
+
 /// \brief Matches any argument of a call expression or a constructor call
 /// expression.
 ///
@@ -4104,6 +4124,51 @@ AST_MATCHER_P(CXXConstructorDecl, forEachConstructorInitializer,
   return Matched;
 }
 
+/// \brief Matches constructor declarations that are copy constructors.
+///
+/// Given
+/// \code
+///   struct S {
+///     S(); // #1
+///     S(const S &); // #2
+///     S(S &&); // #3
+///   };
+/// \endcode
+/// constructorDecl(isCopyConstructor()) will match #2, but not #1 or #3.
+AST_MATCHER(CXXConstructorDecl, isCopyConstructor) {
+  return Node.isCopyConstructor();
+}
+
+/// \brief Matches constructor declarations that are move constructors.
+///
+/// Given
+/// \code
+///   struct S {
+///     S(); // #1
+///     S(const S &); // #2
+///     S(S &&); // #3
+///   };
+/// \endcode
+/// constructorDecl(isMoveConstructor()) will match #3, but not #1 or #2.
+AST_MATCHER(CXXConstructorDecl, isMoveConstructor) {
+  return Node.isMoveConstructor();
+}
+
+/// \brief Matches constructor declarations that are default constructors.
+///
+/// Given
+/// \code
+///   struct S {
+///     S(); // #1
+///     S(const S &); // #2
+///     S(S &&); // #3
+///   };
+/// \endcode
+/// constructorDecl(isDefaultConstructor()) will match #1, but not #2 or #3.
+AST_MATCHER(CXXConstructorDecl, isDefaultConstructor) {
+  return Node.isDefaultConstructor();
+}
+
 /// \brief If the given case statement does not use the GNU case range
 /// extension, matches the constant given in the statement.
 ///
diff --git a/lib/ASTMatchers/Dynamic/Registry.cpp b/lib/ASTMatchers/Dynamic/Registry.cpp
index 8c0832d169..a980110447 100644
--- a/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -240,9 +240,12 @@ RegistryMaps::RegistryMaps() {
   REGISTER_MATCHER(innerType);
   REGISTER_MATCHER(integerLiteral);
   REGISTER_MATCHER(isArrow);
+  REGISTER_MATCHER(isBaseInitializer);
   REGISTER_MATCHER(isCatchAll);
   REGISTER_MATCHER(isConst);
   REGISTER_MATCHER(isConstQualified);
+  REGISTER_MATCHER(isCopyConstructor);
+  REGISTER_MATCHER(isDefaultConstructor);
   REGISTER_MATCHER(isDefinition);
   REGISTER_MATCHER(isDeleted);
   REGISTER_MATCHER(isExceptionVariable);
@@ -259,6 +262,7 @@ RegistryMaps::RegistryMaps() {
   REGISTER_MATCHER(isIntegral);
   REGISTER_MATCHER(isInTemplateInstantiation);
   REGISTER_MATCHER(isListInitialization);
+  REGISTER_MATCHER(isMoveConstructor);
   REGISTER_MATCHER(isOverride);
   REGISTER_MATCHER(isPrivate);
   REGISTER_MATCHER(isProtected);
diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp
index edeebde81e..3383259991 100644
--- a/unittests/ASTMatchers/ASTMatchersTest.cpp
+++ b/unittests/ASTMatchers/ASTMatchersTest.cpp
@@ -1992,6 +1992,29 @@ TEST(ConstructorDeclaration, IsImplicit) {
                       methodDecl(isImplicit(), hasName("operator="))));
 }
 
+TEST(ConstructorDeclaration, Kinds) {
+  EXPECT_TRUE(matches("struct S { S(); };",
+                      constructorDecl(isDefaultConstructor())));
+  EXPECT_TRUE(notMatches("struct S { S(); };",
+                         constructorDecl(isCopyConstructor())));
+  EXPECT_TRUE(notMatches("struct S { S(); };",
+                         constructorDecl(isMoveConstructor())));
+
+  EXPECT_TRUE(notMatches("struct S { S(const S&); };",
+                         constructorDecl(isDefaultConstructor())));
+  EXPECT_TRUE(matches("struct S { S(const S&); };",
+                      constructorDecl(isCopyConstructor())));
+  EXPECT_TRUE(notMatches("struct S { S(const S&); };",
+                         constructorDecl(isMoveConstructor())));
+
+  EXPECT_TRUE(notMatches("struct S { S(S&&); };",
+                         constructorDecl(isDefaultConstructor())));
+  EXPECT_TRUE(notMatches("struct S { S(S&&); };",
+                         constructorDecl(isCopyConstructor())));
+  EXPECT_TRUE(matches("struct S { S(S&&); };",
+                      constructorDecl(isMoveConstructor())));
+}
+
 TEST(DestructorDeclaration, MatchesVirtualDestructor) {
   EXPECT_TRUE(matches("class Foo { virtual ~Foo(); };",
                       destructorDecl(ofClass(hasName("Foo")))));
@@ -2058,6 +2081,24 @@ TEST(HasAnyConstructorInitializer, IsWritten) {
       allOf(forField(hasName("bar_")), unless(isWritten()))))));
 }
 
+TEST(HasAnyConstructorInitializer, IsBaseInitializer) {
+  static const char Code[] =
+      "struct B {};"
+      "struct D : B {"
+      "  int I;"
+      "  D(int i) : I(i) {}"
+      "};"
+      "struct E : B {"
+      "  E() : B() {}"
+      "};";
+  EXPECT_TRUE(matches(Code, constructorDecl(allOf(
+    hasAnyConstructorInitializer(allOf(isBaseInitializer(), isWritten())),
+    hasName("E")))));
+  EXPECT_TRUE(notMatches(Code, constructorDecl(allOf(
+    hasAnyConstructorInitializer(allOf(isBaseInitializer(), isWritten())),
+    hasName("D")))));
+}
+
 TEST(Matcher, NewExpression) {
   StatementMatcher New = newExpr();
 
-- 
2.40.0