From ef7eb024397a6a9d1455b31bc7b10288a817ac3b Mon Sep 17 00:00:00 2001 From: Samuel Benzaquen Date: Fri, 21 Jun 2013 15:51:31 +0000 Subject: [PATCH] Add support for polymorphic matchers. Use runtime type checking to determine the right polymorphic overload to use. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@184558 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LibASTMatchersReference.html | 38 ++++++ docs/tools/dump_ast_matchers.py | 30 ++++- include/clang/ASTMatchers/ASTMatchers.h | 108 ++++++--------- .../clang/ASTMatchers/ASTMatchersInternal.h | 72 +++++++++- include/clang/ASTMatchers/ASTMatchersMacros.h | 67 ++++++---- .../clang/ASTMatchers/Dynamic/Diagnostics.h | 3 +- include/clang/ASTMatchers/Dynamic/Parser.h | 13 +- include/clang/ASTMatchers/Dynamic/Registry.h | 28 ++-- .../clang/ASTMatchers/Dynamic/VariantValue.h | 91 +++++++++++-- lib/ASTMatchers/Dynamic/Diagnostics.cpp | 2 + lib/ASTMatchers/Dynamic/Marshallers.h | 123 ++++++++++++------ lib/ASTMatchers/Dynamic/Parser.cpp | 25 ++-- lib/ASTMatchers/Dynamic/Registry.cpp | 64 ++++----- lib/ASTMatchers/Dynamic/VariantValue.cpp | 82 ++++++++---- unittests/ASTMatchers/ASTMatchersTest.cpp | 7 +- unittests/ASTMatchers/Dynamic/ParserTest.cpp | 34 +++-- .../ASTMatchers/Dynamic/RegistryTest.cpp | 65 +++++---- .../ASTMatchers/Dynamic/VariantValueTest.cpp | 56 ++++---- 18 files changed, 607 insertions(+), 301 deletions(-) diff --git a/docs/LibASTMatchersReference.html b/docs/LibASTMatchersReference.html index 7b70f11845..ee7476ba80 100644 --- a/docs/LibASTMatchersReference.html +++ b/docs/LibASTMatchersReference.html @@ -1547,6 +1547,16 @@ Usable as: Matcher<CXXConstructExpr>argumentCountIsunsigned N +
Checks that a call expression or a constructor call expression has
+a specific number of arguments (including absent default arguments).
+
+Example matches f(0, 0) (matcher = callExpr(argumentCountIs(2)))
+  void f(int x, int y);
+  f(0, 0);
+
+ + Matcher<CXXConstructorDecl>isImplicit
Matches a constructor declaration that has been implicitly added
 by the compiler (eg. implicit defaultcopy constructors).
@@ -2483,6 +2493,34 @@ Usable as: Matcher<CXXConstructExpr>hasAnyArgumentMatcher<Expr> InnerMatcher
+
Matches any argument of a call expression or a constructor call
+expression.
+
+Given
+  void x(int, int, int) { int y; x(1, y, 42); }
+callExpr(hasAnyArgument(declRefExpr()))
+  matches x(1, y, 42)
+with hasAnyArgument(...)
+  matching y
+
+FIXME: Currently this will ignore parentheses and implicit casts on
+the argument before applying the inner matcher. We'll want to remove
+this to allow for greater control by the user once ignoreImplicit()
+has been implemented.
+
+ + +Matcher<CXXConstructExpr>hasArgumentunsigned N, Matcher<Expr> InnerMatcher +
Matches the n'th argument of a call expression or a constructor
+call expression.
+
+Example matches y in x(y)
+    (matcher = callExpr(hasArgument(0, declRefExpr())))
+  void x(int) { int y; x(y); }
+
+ + Matcher<CXXConstructExpr>hasDeclarationMatcher<Decl> InnerMatcher
Matches a node if the declaration associated with that node
 matches the given matcher.
diff --git a/docs/tools/dump_ast_matchers.py b/docs/tools/dump_ast_matchers.py
index 4ed6822be1..267cdb054c 100644
--- a/docs/tools/dump_ast_matchers.py
+++ b/docs/tools/dump_ast_matchers.py
@@ -175,7 +175,31 @@ def act_on_decl(declaration, comment, allowed_types):
                       comment)
       return
 
-    m = re.match(r"""^\s*AST_(POLYMORPHIC_)?MATCHER(_P)?(.?)(?:_OVERLOAD)?\(
+    m = re.match(r"""^\s*AST_POLYMORPHIC_MATCHER(_P)?(.?)(?:_OVERLOAD)?\(
+                          \s*([^\s,]+)\s*,
+                          \s*AST_POLYMORPHIC_SUPPORTED_TYPES_([^(]*)\(([^)]*)\)
+                       (?:,\s*([^\s,]+)\s*
+                          ,\s*([^\s,]+)\s*)?
+                       (?:,\s*([^\s,]+)\s*
+                          ,\s*([^\s,]+)\s*)?
+                       (?:,\s*\d+\s*)?
+                      \)\s*{\s*$""", declaration, flags=re.X)
+
+    if m:
+      p, n, name, n_results, results = m.groups()[0:5]
+      args = m.groups()[5:]
+      result_types = [r.strip() for r in results.split(',')]
+      if allowed_types and allowed_types != result_types:
+        raise Exception('Inconsistent documentation for: %s' % name)
+      if n not in ['', '2']:
+        raise Exception('Cannot parse "%s"' % declaration)
+      args = ', '.join('%s %s' % (args[i], args[i+1])
+                       for i in range(0, len(args), 2) if args[i])
+      for result_type in result_types:
+        add_matcher(result_type, name, args, comment)
+      return
+
+    m = re.match(r"""^\s*AST_MATCHER(_P)?(.?)(?:_OVERLOAD)?\(
                        (?:\s*([^\s,]+)\s*,)?
                           \s*([^\s,]+)\s*
                        (?:,\s*([^\s,]+)\s*
@@ -185,8 +209,8 @@ def act_on_decl(declaration, comment, allowed_types):
                        (?:,\s*\d+\s*)?
                       \)\s*{\s*$""", declaration, flags=re.X)
     if m:
-      p, n, result, name = m.groups()[1:5]
-      args = m.groups()[5:]
+      p, n, result, name = m.groups()[0:4]
+      args = m.groups()[4:]
       if not result:
         if not allowed_types:
           raise Exception('Did not find allowed result types for: %s' % name)
diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h
index 366707b0a4..d6c19b7dfa 100644
--- a/include/clang/ASTMatchers/ASTMatchers.h
+++ b/include/clang/ASTMatchers/ASTMatchers.h
@@ -1451,10 +1451,13 @@ AST_MATCHER_P(NamedDecl, matchesName, std::string, RegExp) {
 ///
 /// Usable as: Matcher, Matcher
 inline internal::PolymorphicMatcherWithParam1<
-    internal::HasOverloadedOperatorNameMatcher, StringRef>
+    internal::HasOverloadedOperatorNameMatcher, StringRef,
+    AST_POLYMORPHIC_SUPPORTED_TYPES_2(CXXOperatorCallExpr, CXXMethodDecl)>
 hasOverloadedOperatorName(const StringRef Name) {
   return internal::PolymorphicMatcherWithParam1<
-      internal::HasOverloadedOperatorNameMatcher, StringRef>(Name);
+      internal::HasOverloadedOperatorNameMatcher, StringRef,
+      AST_POLYMORPHIC_SUPPORTED_TYPES_2(CXXOperatorCallExpr, CXXMethodDecl)>(
+      Name);
 }
 
 /// \brief Matches C++ classes that are directly or indirectly derived from
@@ -1784,11 +1787,9 @@ inline internal::Matcher callee(
 ///  class X {};
 ///  void y(X &x) { x; X z; }
 /// \endcode
-AST_POLYMORPHIC_MATCHER_P(hasType, internal::Matcher,
-                          InnerMatcher) {
-  TOOLING_COMPILE_ASSERT((llvm::is_base_of::value ||
-                          llvm::is_base_of::value),
-                         instantiated_with_wrong_types);
+AST_POLYMORPHIC_MATCHER_P(hasType,
+                          AST_POLYMORPHIC_SUPPORTED_TYPES_2(Expr, ValueDecl),
+                          internal::Matcher, InnerMatcher) {
   return InnerMatcher.matches(Node.getType(), Finder, Builder);
 }
 
@@ -1810,8 +1811,8 @@ AST_POLYMORPHIC_MATCHER_P(hasType, internal::Matcher,
 ///
 /// Usable as: Matcher, Matcher
 inline internal::PolymorphicMatcherWithParam1<
-  internal::matcher_hasType0Matcher,
-  internal::Matcher >
+    internal::matcher_hasType0Matcher, internal::Matcher,
+    AST_POLYMORPHIC_SUPPORTED_TYPES_2(Expr, ValueDecl)>
 hasType(const internal::Matcher &InnerMatcher) {
   return hasType(qualType(hasDeclaration(InnerMatcher)));
 }
@@ -2013,11 +2014,9 @@ AST_MATCHER_P(
 ///   void f(int x, int y);
 ///   f(0, 0);
 /// \endcode
-AST_POLYMORPHIC_MATCHER_P(argumentCountIs, unsigned, N) {
-  TOOLING_COMPILE_ASSERT((llvm::is_base_of::value ||
-                          llvm::is_base_of::value),
-                         instantiated_with_wrong_types);
+AST_POLYMORPHIC_MATCHER_P(argumentCountIs, AST_POLYMORPHIC_SUPPORTED_TYPES_2(
+                                               CallExpr, CXXConstructExpr),
+                          unsigned, N) {
   return Node.getNumArgs() == N;
 }
 
@@ -2030,11 +2029,9 @@ AST_POLYMORPHIC_MATCHER_P(argumentCountIs, unsigned, N) {
 ///   void x(int) { int y; x(y); }
 /// \endcode
 AST_POLYMORPHIC_MATCHER_P2(
-    hasArgument, unsigned, N, internal::Matcher, InnerMatcher) {
-  TOOLING_COMPILE_ASSERT((llvm::is_base_of::value ||
-                         llvm::is_base_of::value),
-                         instantiated_with_wrong_types);
+    hasArgument,
+    AST_POLYMORPHIC_SUPPORTED_TYPES_2(CallExpr, CXXConstructExpr),
+    unsigned, N, internal::Matcher, InnerMatcher) {
   return (N < Node.getNumArgs() &&
           InnerMatcher.matches(
               *Node.getArg(N)->IgnoreParenImpCasts(), Finder, Builder));
@@ -2180,12 +2177,9 @@ AST_MATCHER(CXXConstructorDecl, isImplicit) {
 /// the argument before applying the inner matcher. We'll want to remove
 /// this to allow for greater control by the user once \c ignoreImplicit()
 /// has been implemented.
-AST_POLYMORPHIC_MATCHER_P(hasAnyArgument, internal::Matcher,
-                          InnerMatcher) {
-  TOOLING_COMPILE_ASSERT((llvm::is_base_of::value ||
-                         llvm::is_base_of::value),
-                         instantiated_with_wrong_types);
+AST_POLYMORPHIC_MATCHER_P(hasAnyArgument, AST_POLYMORPHIC_SUPPORTED_TYPES_2(
+                                              CallExpr, CXXConstructExpr),
+                          internal::Matcher, InnerMatcher) {
   for (unsigned I = 0; I < Node.getNumArgs(); ++I) {
     BoundNodesTreeBuilder Result(*Builder);
     if (InnerMatcher.matches(*Node.getArg(I)->IgnoreParenImpCasts(), Finder,
@@ -2280,15 +2274,10 @@ AST_MATCHER(FunctionDecl, isExternC) {
 /// \code
 ///   if (true) {}
 /// \endcode
-AST_POLYMORPHIC_MATCHER_P(hasCondition, internal::Matcher,
-                          InnerMatcher) {
-  TOOLING_COMPILE_ASSERT(
-    (llvm::is_base_of::value) ||
-    (llvm::is_base_of::value) ||
-    (llvm::is_base_of::value) ||
-    (llvm::is_base_of::value) ||
-    (llvm::is_base_of::value),
-    has_condition_requires_if_statement_conditional_operator_or_loop);
+AST_POLYMORPHIC_MATCHER_P(
+    hasCondition, AST_POLYMORPHIC_SUPPORTED_TYPES_5(
+                      IfStmt, ForStmt, WhileStmt, DoStmt, ConditionalOperator),
+    internal::Matcher, InnerMatcher) {
   const Expr *const Condition = Node.getCond();
   return (Condition != NULL &&
           InnerMatcher.matches(*Condition, Finder, Builder));
@@ -2325,18 +2314,15 @@ struct NotEqualsBoundNodePredicate {
 ///     forEachDescendant(declRefExpr(to(decl(equalsBoundNode("d"))))))
 /// will trigger a match for each combination of variable declaration
 /// and reference to that variable declaration within a compound statement.
-AST_POLYMORPHIC_MATCHER_P(equalsBoundNode, std::string, ID) {
+AST_POLYMORPHIC_MATCHER_P(equalsBoundNode, AST_POLYMORPHIC_SUPPORTED_TYPES_4(
+                                               Stmt, Decl, Type, QualType),
+                          std::string, ID) {
   // FIXME: Figure out whether it makes sense to allow this
   // on any other node types.
   // For *Loc it probably does not make sense, as those seem
   // unique. For NestedNameSepcifier it might make sense, as
   // those also have pointer identity, but I'm not sure whether
   // they're ever reused.
-  TOOLING_COMPILE_ASSERT((llvm::is_base_of::value ||
-                          llvm::is_base_of::value ||
-                          llvm::is_base_of::value ||
-                          llvm::is_base_of::value),
-                         equals_bound_node_requires_non_unique_node_class);
   internal::NotEqualsBoundNodePredicate Predicate;
   Predicate.ID = ID;
   Predicate.Node = ast_type_traits::DynTypedNode::create(Node);
@@ -2403,13 +2389,9 @@ AST_MATCHER_P(ArraySubscriptExpr, hasBase,
 ///   matches 'for (;;) {}'
 /// with compoundStmt()
 ///   matching '{}'
-AST_POLYMORPHIC_MATCHER_P(hasBody, internal::Matcher,
-                          InnerMatcher) {
-  TOOLING_COMPILE_ASSERT(
-      (llvm::is_base_of::value) ||
-      (llvm::is_base_of::value) ||
-      (llvm::is_base_of::value),
-      has_body_requires_for_while_or_do_statement);
+AST_POLYMORPHIC_MATCHER_P(
+    hasBody, AST_POLYMORPHIC_SUPPORTED_TYPES_3(DoStmt, ForStmt, WhileStmt),
+    internal::Matcher, InnerMatcher) {
   const Stmt *const Statement = Node.getBody();
   return (Statement != NULL &&
           InnerMatcher.matches(*Statement, Finder, Builder));
@@ -2470,11 +2452,9 @@ equals(const ValueT &Value) {
 /// \code
 ///   !(a || b)
 /// \endcode
-AST_POLYMORPHIC_MATCHER_P(hasOperatorName, std::string, Name) {
-  TOOLING_COMPILE_ASSERT(
-    (llvm::is_base_of::value) ||
-    (llvm::is_base_of::value),
-    has_condition_requires_if_statement_or_conditional_operator);
+AST_POLYMORPHIC_MATCHER_P(hasOperatorName, AST_POLYMORPHIC_SUPPORTED_TYPES_2(
+                                               BinaryOperator, UnaryOperator),
+                          std::string, Name) {
   return Name == Node.getOpcodeStr(Node.getOpcode());
 }
 
@@ -2596,12 +2576,8 @@ AST_MATCHER_P(ConditionalOperator, hasFalseExpression,
 /// \endcode
 ///
 /// Usable as: Matcher, Matcher, Matcher
-AST_POLYMORPHIC_MATCHER(isDefinition) {
-  TOOLING_COMPILE_ASSERT(
-      (llvm::is_base_of::value) ||
-      (llvm::is_base_of::value) ||
-      (llvm::is_base_of::value),
-      is_definition_requires_isThisDeclarationADefinition_method);
+AST_POLYMORPHIC_MATCHER(isDefinition, AST_POLYMORPHIC_SUPPORTED_TYPES_3(
+                                          TagDecl, VarDecl, FunctionDecl)) {
   return Node.isThisDeclarationADefinition();
 }
 
@@ -2834,11 +2810,9 @@ AST_MATCHER_P(UsingShadowDecl, hasTargetDecl,
 ///   does not match, as X is an explicit template specialization.
 ///
 /// Usable as: Matcher, Matcher, Matcher
-AST_POLYMORPHIC_MATCHER(isTemplateInstantiation) {
-  TOOLING_COMPILE_ASSERT((llvm::is_base_of::value) ||
-                         (llvm::is_base_of::value) ||
-                         (llvm::is_base_of::value),
-                         requires_getTemplateSpecializationKind_method);
+AST_POLYMORPHIC_MATCHER(
+    isTemplateInstantiation,
+    AST_POLYMORPHIC_SUPPORTED_TYPES_3(FunctionDecl, VarDecl, CXXRecordDecl)) {
   return (Node.getTemplateSpecializationKind() == TSK_ImplicitInstantiation ||
           Node.getTemplateSpecializationKind() ==
           TSK_ExplicitInstantiationDefinition);
@@ -2856,11 +2830,9 @@ AST_POLYMORPHIC_MATCHER(isTemplateInstantiation) {
 ///   matches the specialization A().
 ///
 /// Usable as: Matcher, Matcher, Matcher
-AST_POLYMORPHIC_MATCHER(isExplicitTemplateSpecialization) {
-  TOOLING_COMPILE_ASSERT((llvm::is_base_of::value) ||
-                         (llvm::is_base_of::value) ||
-                         (llvm::is_base_of::value),
-                         requires_getTemplateSpecializationKind_method);
+AST_POLYMORPHIC_MATCHER(
+    isExplicitTemplateSpecialization,
+    AST_POLYMORPHIC_SUPPORTED_TYPES_3(FunctionDecl, VarDecl, CXXRecordDecl)) {
   return (Node.getTemplateSpecializationKind() == TSK_ExplicitSpecialization);
 }
 
diff --git a/include/clang/ASTMatchers/ASTMatchersInternal.h b/include/clang/ASTMatchers/ASTMatchersInternal.h
index 9e6d4bf348..f29ea0e1dc 100644
--- a/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -761,6 +761,58 @@ private:
   const Matcher InnerMatcher;
 };
 
+/// \brief A simple type-list implementation.
+///
+/// It is implemented as a flat struct with a maximum number of arguments to
+/// simplify compiler error messages.
+/// However, it is used as a "linked list" of types.
+///
+/// Note: If you need to extend for more types, add them as template arguments
+///       and to the "typedef TypeList<...> tail" below. Nothing else is needed.
+template 
+struct TypeList {
+  /// \brief The first type on the list.
+  typedef T1 head;
+
+  /// \brief A sub list with the tail. ie everything but the head.
+  ///
+  /// This type is used to do recursion. TypeList<>/EmptyTypeList indicates the
+  /// end of the list.
+  typedef TypeList tail;
+
+  /// \brief Helper meta-function to determine if some type \c T is present or
+  ///   a parent type in the list.
+  template  struct ContainsSuperOf {
+    static const bool value = llvm::is_base_of::value ||
+                              tail::template ContainsSuperOf::value;
+  };
+};
+
+/// \brief Specialization of ContainsSuperOf for the empty list.
+template <> template  struct TypeList<>::ContainsSuperOf {
+  static const bool value = false;
+};
+
+/// \brief The empty type list.
+typedef TypeList<> EmptyTypeList;
+
+/// \brief A "type list" that contains all types.
+///
+/// Useful for matchers like \c anything and \c unless.
+typedef TypeList AllNodeBaseTypes;
+
+/// \brief Helper meta-function to extract the argument out of a function of
+///   type void(Arg).
+///
+/// See AST_POLYMORPHIC_SUPPORTED_TYPES_* for details.
+template  struct ExtractFunctionArgMeta;
+template  struct ExtractFunctionArgMeta {
+  typedef T type;
+};
+
 /// \brief A PolymorphicMatcherWithParamN object can be
 /// created from N parameters p1, ..., pN (of type P1, ..., PN) and
 /// used as a Matcher where a MatcherT(p1, ..., pN)
@@ -773,24 +825,33 @@ private:
 /// - PolymorphicMatcherWithParam1(42)
 ///   creates an object that can be used as a Matcher for any type T
 ///   where a ValueEqualsMatcher(42) can be constructed.
-template