From ef7eb024397a6a9d1455b31bc7b10288a817ac3b Mon Sep 17 00:00:00 2001 From: Samuel Benzaquen <sbenza@google.com> 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 7b70f118454..ee7476ba80d 100644 --- a/docs/LibASTMatchersReference.html +++ b/docs/LibASTMatchersReference.html @@ -1547,6 +1547,16 @@ Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Charac </pre></td></tr> +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXConstructExpr.html">CXXConstructExpr</a>></td><td class="name" onclick="toggle('argumentCountIs1')"><a name="argumentCountIs1Anchor">argumentCountIs</a></td><td>unsigned N</td></tr> +<tr><td colspan="4" class="doc" id="argumentCountIs1"><pre>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); +</pre></td></tr> + + <tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXConstructorDecl.html">CXXConstructorDecl</a>></td><td class="name" onclick="toggle('isImplicit0')"><a name="isImplicit0Anchor">isImplicit</a></td><td></td></tr> <tr><td colspan="4" class="doc" id="isImplicit0"><pre>Matches a constructor declaration that has been implicitly added by the compiler (eg. implicit defaultcopy constructors). @@ -2483,6 +2493,34 @@ Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1BlockP </pre></td></tr> +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXConstructExpr.html">CXXConstructExpr</a>></td><td class="name" onclick="toggle('hasAnyArgument1')"><a name="hasAnyArgument1Anchor">hasAnyArgument</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr> +<tr><td colspan="4" class="doc" id="hasAnyArgument1"><pre>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. +</pre></td></tr> + + +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXConstructExpr.html">CXXConstructExpr</a>></td><td class="name" onclick="toggle('hasArgument1')"><a name="hasArgument1Anchor">hasArgument</a></td><td>unsigned N, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr> +<tr><td colspan="4" class="doc" id="hasArgument1"><pre>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); } +</pre></td></tr> + + <tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXConstructExpr.html">CXXConstructExpr</a>></td><td class="name" onclick="toggle('hasDeclaration3')"><a name="hasDeclaration3Anchor">hasDeclaration</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>> InnerMatcher</td></tr> <tr><td colspan="4" class="doc" id="hasDeclaration3"><pre>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 4ed6822be13..267cdb054c9 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 366707b0a44..d6c19b7dfa2 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<CXXOperatorCallExpr>, Matcher<CXXMethodDecl> 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<CallExpr> callee( /// class X {}; /// void y(X &x) { x; X z; } /// \endcode -AST_POLYMORPHIC_MATCHER_P(hasType, internal::Matcher<QualType>, - InnerMatcher) { - TOOLING_COMPILE_ASSERT((llvm::is_base_of<Expr, NodeType>::value || - llvm::is_base_of<ValueDecl, NodeType>::value), - instantiated_with_wrong_types); +AST_POLYMORPHIC_MATCHER_P(hasType, + AST_POLYMORPHIC_SUPPORTED_TYPES_2(Expr, ValueDecl), + internal::Matcher<QualType>, InnerMatcher) { return InnerMatcher.matches(Node.getType(), Finder, Builder); } @@ -1810,8 +1811,8 @@ AST_POLYMORPHIC_MATCHER_P(hasType, internal::Matcher<QualType>, /// /// Usable as: Matcher<Expr>, Matcher<ValueDecl> inline internal::PolymorphicMatcherWithParam1< - internal::matcher_hasType0Matcher, - internal::Matcher<QualType> > + internal::matcher_hasType0Matcher, internal::Matcher<QualType>, + AST_POLYMORPHIC_SUPPORTED_TYPES_2(Expr, ValueDecl)> hasType(const internal::Matcher<Decl> &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<CallExpr, NodeType>::value || - llvm::is_base_of<CXXConstructExpr, - NodeType>::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<Expr>, InnerMatcher) { - TOOLING_COMPILE_ASSERT((llvm::is_base_of<CallExpr, NodeType>::value || - llvm::is_base_of<CXXConstructExpr, - NodeType>::value), - instantiated_with_wrong_types); + hasArgument, + AST_POLYMORPHIC_SUPPORTED_TYPES_2(CallExpr, CXXConstructExpr), + unsigned, N, internal::Matcher<Expr>, 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<Expr>, - InnerMatcher) { - TOOLING_COMPILE_ASSERT((llvm::is_base_of<CallExpr, NodeType>::value || - llvm::is_base_of<CXXConstructExpr, - NodeType>::value), - instantiated_with_wrong_types); +AST_POLYMORPHIC_MATCHER_P(hasAnyArgument, AST_POLYMORPHIC_SUPPORTED_TYPES_2( + CallExpr, CXXConstructExpr), + internal::Matcher<Expr>, 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<Expr>, - InnerMatcher) { - TOOLING_COMPILE_ASSERT( - (llvm::is_base_of<IfStmt, NodeType>::value) || - (llvm::is_base_of<ForStmt, NodeType>::value) || - (llvm::is_base_of<WhileStmt, NodeType>::value) || - (llvm::is_base_of<DoStmt, NodeType>::value) || - (llvm::is_base_of<ConditionalOperator, NodeType>::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<Expr>, 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<Stmt, NodeType>::value || - llvm::is_base_of<Decl, NodeType>::value || - llvm::is_base_of<Type, NodeType>::value || - llvm::is_base_of<QualType, NodeType>::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<Stmt>, - InnerMatcher) { - TOOLING_COMPILE_ASSERT( - (llvm::is_base_of<DoStmt, NodeType>::value) || - (llvm::is_base_of<ForStmt, NodeType>::value) || - (llvm::is_base_of<WhileStmt, NodeType>::value), - has_body_requires_for_while_or_do_statement); +AST_POLYMORPHIC_MATCHER_P( + hasBody, AST_POLYMORPHIC_SUPPORTED_TYPES_3(DoStmt, ForStmt, WhileStmt), + internal::Matcher<Stmt>, 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<BinaryOperator, NodeType>::value) || - (llvm::is_base_of<UnaryOperator, NodeType>::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<TagDecl>, Matcher<VarDecl>, Matcher<FunctionDecl> -AST_POLYMORPHIC_MATCHER(isDefinition) { - TOOLING_COMPILE_ASSERT( - (llvm::is_base_of<TagDecl, NodeType>::value) || - (llvm::is_base_of<VarDecl, NodeType>::value) || - (llvm::is_base_of<FunctionDecl, NodeType>::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<A> is an explicit template specialization. /// /// Usable as: Matcher<FunctionDecl>, Matcher<VarDecl>, Matcher<CXXRecordDecl> -AST_POLYMORPHIC_MATCHER(isTemplateInstantiation) { - TOOLING_COMPILE_ASSERT((llvm::is_base_of<FunctionDecl, NodeType>::value) || - (llvm::is_base_of<VarDecl, NodeType>::value) || - (llvm::is_base_of<CXXRecordDecl, NodeType>::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<int>(). /// /// Usable as: Matcher<FunctionDecl>, Matcher<VarDecl>, Matcher<CXXRecordDecl> -AST_POLYMORPHIC_MATCHER(isExplicitTemplateSpecialization) { - TOOLING_COMPILE_ASSERT((llvm::is_base_of<FunctionDecl, NodeType>::value) || - (llvm::is_base_of<VarDecl, NodeType>::value) || - (llvm::is_base_of<CXXRecordDecl, NodeType>::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 9e6d4bf3482..f29ea0e1dcc 100644 --- a/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -761,6 +761,58 @@ private: const Matcher<T> 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 <typename T1 = void, typename T2 = void, typename T3 = void, + typename T4 = void, typename T5 = void, typename T6 = void, + typename T7 = void, typename T8 = void> +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<T2, T3, T4, T5, T6, T7, T8> tail; + + /// \brief Helper meta-function to determine if some type \c T is present or + /// a parent type in the list. + template <typename T> struct ContainsSuperOf { + static const bool value = llvm::is_base_of<head, T>::value || + tail::template ContainsSuperOf<T>::value; + }; +}; + +/// \brief Specialization of ContainsSuperOf for the empty list. +template <> template <typename T> 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<Decl, Stmt, NestedNameSpecifier, NestedNameSpecifierLoc, + QualType, Type, TypeLoc, CXXCtorInitializer> 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 <class T> struct ExtractFunctionArgMeta; +template <class T> struct ExtractFunctionArgMeta<void(T)> { + typedef T type; +}; + /// \brief A PolymorphicMatcherWithParamN<MatcherT, P1, ..., PN> object can be /// created from N parameters p1, ..., pN (of type P1, ..., PN) and /// used as a Matcher<T> where a MatcherT<T, P1, ..., PN>(p1, ..., pN) @@ -773,24 +825,33 @@ private: /// - PolymorphicMatcherWithParam1<ValueEqualsMatcher, int>(42) /// creates an object that can be used as a Matcher<T> for any type T /// where a ValueEqualsMatcher<T, int>(42) can be constructed. -template <template <typename T> class MatcherT> +template <template <typename T> class MatcherT, + typename ReturnTypesF = void(AllNodeBaseTypes)> class PolymorphicMatcherWithParam0 { public: + typedef typename ExtractFunctionArgMeta<ReturnTypesF>::type ReturnTypes; template <typename T> operator Matcher<T>() const { + TOOLING_COMPILE_ASSERT(ReturnTypes::template ContainsSuperOf<T>::value, + right_polymorphic_conversion); return Matcher<T>(new MatcherT<T>()); } }; template <template <typename T, typename P1> class MatcherT, - typename P1> + typename P1, + typename ReturnTypesF = void(AllNodeBaseTypes)> class PolymorphicMatcherWithParam1 { public: explicit PolymorphicMatcherWithParam1(const P1 &Param1) : Param1(Param1) {} + typedef typename ExtractFunctionArgMeta<ReturnTypesF>::type ReturnTypes; + template <typename T> operator Matcher<T>() const { + TOOLING_COMPILE_ASSERT(ReturnTypes::template ContainsSuperOf<T>::value, + right_polymorphic_conversion); return Matcher<T>(new MatcherT<T, P1>(Param1)); } @@ -799,14 +860,19 @@ private: }; template <template <typename T, typename P1, typename P2> class MatcherT, - typename P1, typename P2> + typename P1, typename P2, + typename ReturnTypesF = void(AllNodeBaseTypes)> class PolymorphicMatcherWithParam2 { public: PolymorphicMatcherWithParam2(const P1 &Param1, const P2 &Param2) : Param1(Param1), Param2(Param2) {} + typedef typename ExtractFunctionArgMeta<ReturnTypesF>::type ReturnTypes; + template <typename T> operator Matcher<T>() const { + TOOLING_COMPILE_ASSERT(ReturnTypes::template ContainsSuperOf<T>::value, + right_polymorphic_conversion); return Matcher<T>(new MatcherT<T, P1, P2>(Param1, Param2)); } diff --git a/include/clang/ASTMatchers/ASTMatchersMacros.h b/include/clang/ASTMatchers/ASTMatchersMacros.h index f5ca26bca73..91e254eb571 100644 --- a/include/clang/ASTMatchers/ASTMatchersMacros.h +++ b/include/clang/ASTMatchers/ASTMatchersMacros.h @@ -155,16 +155,34 @@ const Type &Node, ASTMatchFinder *Finder, \ BoundNodesTreeBuilder *Builder) const +/// \brief Construct a type-list to be passed to the AST_POLYMORPHIC_MATCHER* +/// macros. +/// +/// You can't pass something like \c TypeList<Foo, Bar> to a macro, because it +/// will look at that as two arguments. However, you can pass +/// \c void(TypeList<Foo, Bar>), which works thanks to the parenthesis. +/// The \c PolymorphicMatcherWithParam* classes will unpack the function type to +/// extract the TypeList object. +#define AST_POLYMORPHIC_SUPPORTED_TYPES_2(t1, t2) \ + void(internal::TypeList<t1, t2>) +#define AST_POLYMORPHIC_SUPPORTED_TYPES_3(t1, t2, t3) \ + void(internal::TypeList<t1, t2, t3>) +#define AST_POLYMORPHIC_SUPPORTED_TYPES_4(t1, t2, t3, t4) \ + void(internal::TypeList<t1, t2, t3, t4>) +#define AST_POLYMORPHIC_SUPPORTED_TYPES_5(t1, t2, t3, t4, t5) \ + void(internal::TypeList<t1, t2, t3, t4, t5>) + /// \brief AST_POLYMORPHIC_MATCHER(DefineMatcher) { ... } /// defines a single-parameter function named DefineMatcher() that is /// polymorphic in the return type. /// /// The variables are the same as for AST_MATCHER, but NodeType will be deduced /// from the calling context. -#define AST_POLYMORPHIC_MATCHER(DefineMatcher) \ - AST_POLYMORPHIC_MATCHER_OVERLOAD(DefineMatcher, 0) +#define AST_POLYMORPHIC_MATCHER(DefineMatcher, ReturnTypesF) \ + AST_POLYMORPHIC_MATCHER_OVERLOAD(DefineMatcher, ReturnTypesF, 0) -#define AST_POLYMORPHIC_MATCHER_OVERLOAD(DefineMatcher, OverloadId) \ +#define AST_POLYMORPHIC_MATCHER_OVERLOAD(DefineMatcher, ReturnTypesF, \ + OverloadId) \ namespace internal { \ template <typename NodeType> \ class matcher_##DefineMatcher##OverloadId##Matcher \ @@ -175,9 +193,11 @@ }; \ } \ inline internal::PolymorphicMatcherWithParam0< \ - internal::matcher_##DefineMatcher##OverloadId##Matcher> DefineMatcher() {\ + internal::matcher_##DefineMatcher##OverloadId##Matcher, ReturnTypesF> \ + DefineMatcher() { \ return internal::PolymorphicMatcherWithParam0< \ - internal::matcher_##DefineMatcher##OverloadId##Matcher>(); \ + internal::matcher_##DefineMatcher##OverloadId##Matcher, \ + ReturnTypesF>(); \ } \ template <typename NodeType> \ bool internal::matcher_##DefineMatcher##OverloadId##Matcher< \ @@ -193,11 +213,13 @@ /// of the matcher Matcher<NodeType> returned by the function matcher(). /// /// FIXME: Pull out common code with above macro? -#define AST_POLYMORPHIC_MATCHER_P(DefineMatcher, ParamType, Param) \ - AST_POLYMORPHIC_MATCHER_P_OVERLOAD(DefineMatcher, ParamType, Param, 0) +#define AST_POLYMORPHIC_MATCHER_P(DefineMatcher, ReturnTypesF, ParamType, \ + Param) \ + AST_POLYMORPHIC_MATCHER_P_OVERLOAD(DefineMatcher, ReturnTypesF, ParamType, \ + Param, 0) -#define AST_POLYMORPHIC_MATCHER_P_OVERLOAD(DefineMatcher, ParamType, Param, \ - OverloadId) \ +#define AST_POLYMORPHIC_MATCHER_P_OVERLOAD(DefineMatcher, ReturnTypesF, \ + ParamType, Param, OverloadId) \ namespace internal { \ template <typename NodeType, typename ParamT> \ class matcher_##DefineMatcher##OverloadId##Matcher \ @@ -214,11 +236,11 @@ }; \ } \ inline internal::PolymorphicMatcherWithParam1< \ - internal::matcher_##DefineMatcher##OverloadId##Matcher, ParamType> \ - DefineMatcher(const ParamType &Param) { \ + internal::matcher_##DefineMatcher##OverloadId##Matcher, ParamType, \ + ReturnTypesF> DefineMatcher(const ParamType & Param) { \ return internal::PolymorphicMatcherWithParam1< \ - internal::matcher_##DefineMatcher##OverloadId##Matcher, ParamType>( \ - Param); \ + internal::matcher_##DefineMatcher##OverloadId##Matcher, ParamType, \ + ReturnTypesF>(Param); \ } \ template <typename NodeType, typename ParamT> \ bool internal::matcher_##DefineMatcher##OverloadId##Matcher< \ @@ -233,13 +255,14 @@ /// The variables are the same as for AST_MATCHER_P2, with the /// addition of NodeType, which specifies the node type of the matcher /// Matcher<NodeType> returned by the function DefineMatcher(). -#define AST_POLYMORPHIC_MATCHER_P2(DefineMatcher, ParamType1, Param1, \ - ParamType2, Param2) \ - AST_POLYMORPHIC_MATCHER_P2_OVERLOAD(DefineMatcher, ParamType1, Param1, \ - ParamType2, Param2, 0) +#define AST_POLYMORPHIC_MATCHER_P2(DefineMatcher, ReturnTypesF, ParamType1, \ + Param1, ParamType2, Param2) \ + AST_POLYMORPHIC_MATCHER_P2_OVERLOAD(DefineMatcher, ReturnTypesF, ParamType1, \ + Param1, ParamType2, Param2, 0) -#define AST_POLYMORPHIC_MATCHER_P2_OVERLOAD(DefineMatcher, ParamType1, Param1, \ - ParamType2, Param2, OverloadId) \ +#define AST_POLYMORPHIC_MATCHER_P2_OVERLOAD(DefineMatcher, ReturnTypesF, \ + ParamType1, Param1, ParamType2, \ + Param2, OverloadId) \ namespace internal { \ template <typename NodeType, typename ParamT1, typename ParamT2> \ class matcher_##DefineMatcher##OverloadId##Matcher \ @@ -258,11 +281,11 @@ } \ inline internal::PolymorphicMatcherWithParam2< \ internal::matcher_##DefineMatcher##OverloadId##Matcher, ParamType1, \ - ParamType2> \ - DefineMatcher(const ParamType1 &Param1, const ParamType2 &Param2) { \ + ParamType2, ReturnTypesF> DefineMatcher(const ParamType1 & Param1, \ + const ParamType2 & Param2) { \ return internal::PolymorphicMatcherWithParam2< \ internal::matcher_##DefineMatcher##OverloadId##Matcher, ParamType1, \ - ParamType2>(Param1, Param2); \ + ParamType2, ReturnTypesF>(Param1, Param2); \ } \ template <typename NodeType, typename ParamT1, typename ParamT2> \ bool internal::matcher_##DefineMatcher##OverloadId##Matcher< \ diff --git a/include/clang/ASTMatchers/Dynamic/Diagnostics.h b/include/clang/ASTMatchers/Dynamic/Diagnostics.h index 21e08ff2da8..973d54cead3 100644 --- a/include/clang/ASTMatchers/Dynamic/Diagnostics.h +++ b/include/clang/ASTMatchers/Dynamic/Diagnostics.h @@ -70,7 +70,8 @@ class Diagnostics { ET_ParserInvalidToken = 108, ET_ParserMalformedBindExpr = 109, ET_ParserTrailingCode = 110, - ET_ParserUnsignedError = 111 + ET_ParserUnsignedError = 111, + ET_ParserOverloadedType = 112 }; /// \brief Helper stream class. diff --git a/include/clang/ASTMatchers/Dynamic/Parser.h b/include/clang/ASTMatchers/Dynamic/Parser.h index 51dbc8a5a43..b3fd7d607fb 100644 --- a/include/clang/ASTMatchers/Dynamic/Parser.h +++ b/include/clang/ASTMatchers/Dynamic/Parser.h @@ -74,13 +74,14 @@ public: /// /// \param Args The argument list for the matcher. /// - /// \return The matcher object constructed by the processor, or NULL - /// if an error occurred. In that case, \c Error will contain a + /// \return The matcher objects constructed by the processor, or an empty + /// list if an error occurred. In that case, \c Error will contain a /// description of the error. - /// The caller takes ownership of the DynTypedMatcher object returned. - virtual DynTypedMatcher *actOnMatcherExpression( - StringRef MatcherName, const SourceRange &NameRange, StringRef BindID, - ArrayRef<ParserValue> Args, Diagnostics *Error) = 0; + virtual MatcherList actOnMatcherExpression(StringRef MatcherName, + const SourceRange &NameRange, + StringRef BindID, + ArrayRef<ParserValue> Args, + Diagnostics *Error) = 0; }; /// \brief Parse a matcher expression, creating matchers from the registry. diff --git a/include/clang/ASTMatchers/Dynamic/Registry.h b/include/clang/ASTMatchers/Dynamic/Registry.h index dede8df776f..a75f29641ab 100644 --- a/include/clang/ASTMatchers/Dynamic/Registry.h +++ b/include/clang/ASTMatchers/Dynamic/Registry.h @@ -43,26 +43,26 @@ public: /// values must be valid for the matcher requested. Otherwise, the function /// will return an error. /// - /// \return The matcher if no error was found. NULL if the matcher is not - // found, or if the number of arguments or argument types do not - /// match the signature. In that case \c Error will contain the description - /// of the error. - static DynTypedMatcher *constructMatcher(StringRef MatcherName, - const SourceRange &NameRange, - ArrayRef<ParserValue> Args, - Diagnostics *Error); + /// \return The matcher objects constructed if no error was found. + /// An empty list if the matcher is not found, or if the number of + /// arguments or argument types do not match the signature. + /// In that case \c Error will contain the description of the error. + static MatcherList constructMatcher(StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef<ParserValue> Args, + Diagnostics *Error); /// \brief Construct a matcher from the registry and bind it. /// /// Similar the \c constructMatcher() above, but it then tries to bind the /// matcher to the specified \c BindID. /// If the matcher is not bindable, it sets an error in \c Error and returns - /// \c NULL. - static DynTypedMatcher *constructBoundMatcher(StringRef MatcherName, - const SourceRange &NameRange, - StringRef BindID, - ArrayRef<ParserValue> Args, - Diagnostics *Error); + /// an empty list. + static MatcherList constructBoundMatcher(StringRef MatcherName, + const SourceRange &NameRange, + StringRef BindID, + ArrayRef<ParserValue> Args, + Diagnostics *Error); private: Registry() LLVM_DELETED_FUNCTION; diff --git a/include/clang/ASTMatchers/Dynamic/VariantValue.h b/include/clang/ASTMatchers/Dynamic/VariantValue.h index c2e60e1ce9e..44f497248ae 100644 --- a/include/clang/ASTMatchers/Dynamic/VariantValue.h +++ b/include/clang/ASTMatchers/Dynamic/VariantValue.h @@ -17,6 +17,8 @@ #ifndef LLVM_CLANG_AST_MATCHERS_DYNAMIC_VARIANT_VALUE_H #define LLVM_CLANG_AST_MATCHERS_DYNAMIC_VARIANT_VALUE_H +#include <vector> + #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/ASTMatchers/ASTMatchersInternal.h" #include "llvm/ADT/Twine.h" @@ -28,6 +30,73 @@ namespace dynamic { using ast_matchers::internal::DynTypedMatcher; +/// \brief A list of \c DynTypedMatcher objects. +/// +/// The purpose of this list is to wrap multiple different matchers and +/// provide the right one when calling \c hasTypedMatcher/getTypedMatcher. +class MatcherList { +public: + /// \brief An empty list. + MatcherList(); + /// \brief Clones the matcher objects. + MatcherList(const MatcherList &Other); + /// \brief Clones the provided matcher. + MatcherList(const DynTypedMatcher &Matcher); + ~MatcherList(); + + MatcherList &operator=(const MatcherList &Other); + + /// \brief Add a matcher to this list. The matcher is cloned. + void add(const DynTypedMatcher &Matcher); + + /// \brief Empties the list. + void reset(); + + /// \brief Whether the list is empty. + bool empty() const { return List.empty(); } + + ArrayRef<const DynTypedMatcher *> matchers() const { return List; } + + /// \brief Determines if any of the contained matchers can be converted + /// to \c Matcher<T>. + /// + /// Returns true if one, and only one, of the contained matchers can be + /// converted to \c Matcher<T>. If there are more than one that can, the + /// result would be ambigous and false is returned. + template <class T> + bool hasTypedMatcher() const { + size_t Matches = 0; + for (size_t I = 0, E = List.size(); I != E; ++I) { + Matches += ast_matchers::internal::Matcher<T>::canConstructFrom(*List[I]); + } + return Matches == 1; + } + + /// \brief Wrap the correct matcher as a \c Matcher<T>. + /// + /// Selects the appropriate matcher from the list and returns it as a + /// \c Matcher<T>. + /// Asserts that \c hasTypedMatcher<T>() is true. + template <class T> + ast_matchers::internal::Matcher<T> getTypedMatcher() const { + assert(hasTypedMatcher<T>()); + for (size_t I = 0, E = List.size(); I != E; ++I) { + if (ast_matchers::internal::Matcher<T>::canConstructFrom(*List[I])) + return ast_matchers::internal::Matcher<T>::constructFrom(*List[I]); + } + llvm_unreachable("!hasTypedMatcher<T>()"); + } + + /// \brief String representation of the type of the value. + /// + /// If there are more than one matcher on the list, the string will show all + /// the types. + std::string getTypeAsString() const; + +private: + std::vector<const DynTypedMatcher *> List; +}; + /// \brief Variant value class. /// /// Basically, a tagged union with value type semantics. @@ -39,7 +108,7 @@ using ast_matchers::internal::DynTypedMatcher; /// Supported types: /// - \c unsigned /// - \c std::string -/// - \c DynTypedMatcher, and any \c Matcher<T> +/// - \c MatcherList (\c DynTypedMatcher / \c Matcher<T>) class VariantValue { public: VariantValue() : Type(VT_Nothing) {} @@ -52,6 +121,7 @@ public: VariantValue(unsigned Unsigned); VariantValue(const std::string &String); VariantValue(const DynTypedMatcher &Matcher); + VariantValue(const MatcherList &Matchers); /// \brief Unsigned value functions. bool isUnsigned() const; @@ -64,22 +134,19 @@ public: void setString(const std::string &String); /// \brief Matcher value functions. - bool isMatcher() const; - const DynTypedMatcher &getMatcher() const; - void setMatcher(const DynTypedMatcher &Matcher); - /// \brief Set the value to be \c Matcher by taking ownership of the object. - void takeMatcher(DynTypedMatcher *Matcher); + bool isMatchers() const; + const MatcherList &getMatchers() const; + void setMatchers(const MatcherList &Matchers); - /// \brief Specialized Matcher<T> functions. + /// \brief Shortcut functions. template <class T> bool hasTypedMatcher() const { - return isMatcher() && - ast_matchers::internal::Matcher<T>::canConstructFrom(getMatcher()); + return isMatchers() && getMatchers().hasTypedMatcher<T>(); } template <class T> ast_matchers::internal::Matcher<T> getTypedMatcher() const { - return ast_matchers::internal::Matcher<T>::constructFrom(getMatcher()); + return getMatchers().getTypedMatcher<T>(); } /// \brief String representation of the type of the value. @@ -93,14 +160,14 @@ private: VT_Nothing, VT_Unsigned, VT_String, - VT_Matcher + VT_Matchers }; /// \brief All supported value types. union AllValues { unsigned Unsigned; std::string *String; - DynTypedMatcher *Matcher; + MatcherList *Matchers; }; ValueType Type; diff --git a/lib/ASTMatchers/Dynamic/Diagnostics.cpp b/lib/ASTMatchers/Dynamic/Diagnostics.cpp index 6b1d6609a21..1ca1f365398 100644 --- a/lib/ASTMatchers/Dynamic/Diagnostics.cpp +++ b/lib/ASTMatchers/Dynamic/Diagnostics.cpp @@ -64,6 +64,8 @@ StringRef ErrorTypeToString(Diagnostics::ErrorType Type) { return "Expected end of code."; case Diagnostics::ET_ParserUnsignedError: return "Error parsing unsigned token: <$0>"; + case Diagnostics::ET_ParserOverloadedType: + return "Input value has unresolved overloaded type: $0"; case Diagnostics::ET_None: return "<N/A>"; diff --git a/lib/ASTMatchers/Dynamic/Marshallers.h b/lib/ASTMatchers/Dynamic/Marshallers.h index aceddc987fe..c3a5260eb43 100644 --- a/lib/ASTMatchers/Dynamic/Marshallers.h +++ b/lib/ASTMatchers/Dynamic/Marshallers.h @@ -49,6 +49,10 @@ template <> struct ArgTypeTraits<std::string> { } }; +template <> +struct ArgTypeTraits<StringRef> : public ArgTypeTraits<std::string> { +}; + template <class T> struct ArgTypeTraits<ast_matchers::internal::Matcher<T> > { static std::string asString() { return (Twine("Matcher<") + @@ -78,9 +82,9 @@ template <> struct ArgTypeTraits<unsigned> { class MatcherCreateCallback { public: virtual ~MatcherCreateCallback() {} - virtual DynTypedMatcher *run(const SourceRange &NameRange, - ArrayRef<ParserValue> Args, - Diagnostics *Error) const = 0; + virtual MatcherList run(const SourceRange &NameRange, + ArrayRef<ParserValue> Args, + Diagnostics *Error) const = 0; }; /// \brief Simple callback implementation. Marshaller and function are provided. @@ -95,10 +99,10 @@ public: /// FIXME: Use void(*)() as FuncType on this interface to remove the template /// argument of this class. The marshaller can cast the function pointer back /// to the original type. - typedef DynTypedMatcher *(*MarshallerType)(FuncType, StringRef, - const SourceRange &, - ArrayRef<ParserValue>, - Diagnostics *); + typedef MatcherList (*MarshallerType)(FuncType, StringRef, + const SourceRange &, + ArrayRef<ParserValue>, + Diagnostics *); /// \param Marshaller Function to unpack the arguments and call \c Func /// \param Func Matcher construct function. This is the function that @@ -107,8 +111,8 @@ public: StringRef MatcherName) : Marshaller(Marshaller), Func(Func), MatcherName(MatcherName.str()) {} - DynTypedMatcher *run(const SourceRange &NameRange, - ArrayRef<ParserValue> Args, Diagnostics *Error) const { + MatcherList run(const SourceRange &NameRange, ArrayRef<ParserValue> Args, + Diagnostics *Error) const { return Marshaller(Func, MatcherName, NameRange, Args, Error); } @@ -127,16 +131,16 @@ private: /// object file. class FreeFuncMatcherCreateCallback : public MatcherCreateCallback { public: - typedef DynTypedMatcher *(*RunFunc)(StringRef MatcherName, - const SourceRange &NameRange, - ArrayRef<ParserValue> Args, - Diagnostics *Error); + typedef MatcherList (*RunFunc)(StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef<ParserValue> Args, + Diagnostics *Error); FreeFuncMatcherCreateCallback(RunFunc Func, StringRef MatcherName) : Func(Func), MatcherName(MatcherName.str()) {} - DynTypedMatcher *run(const SourceRange &NameRange, ArrayRef<ParserValue> Args, - Diagnostics *Error) const { + MatcherList run(const SourceRange &NameRange, ArrayRef<ParserValue> Args, + Diagnostics *Error) const { return Func(MatcherName, NameRange, Args, Error); } @@ -150,7 +154,7 @@ private: if (Args.size() != count) { \ Error->pushErrorFrame(NameRange, Error->ET_RegistryWrongArgCount) \ << count << Args.size(); \ - return NULL; \ + return MatcherList(); \ } #define CHECK_ARG_TYPE(index, type) \ @@ -158,43 +162,78 @@ private: Error->pushErrorFrame(Args[index].Range, Error->ET_RegistryWrongArgType) \ << (index + 1) << ArgTypeTraits<type>::asString() \ << Args[index].Value.getTypeAsString(); \ - return NULL; \ + return MatcherList(); \ } +/// \brief Helper methods to extract and merge all possible typed matchers +/// out of the polymorphic object. +template <class PolyMatcher> +static void mergePolyMatchers(const PolyMatcher &Poly, MatcherList *Out, + ast_matchers::internal::EmptyTypeList) {} + +template <class PolyMatcher, class TypeList> +static void mergePolyMatchers(const PolyMatcher &Poly, MatcherList *Out, + TypeList) { + Out->add(ast_matchers::internal::Matcher<typename TypeList::head>(Poly)); + mergePolyMatchers(Poly, Out, typename TypeList::tail()); +} + +/// \brief Convert the return values of the functions into a MatcherList. +/// +/// There are 2 cases right now: The return value is a Matcher<T> or is a +/// polymorphic matcher. For the former, we just construct the MatcherList. For +/// the latter, we instantiate all the possible Matcher<T> of the poly matcher. +template <typename T> +static MatcherList +outvalueToMatcherList(const ast_matchers::internal::Matcher<T> &Matcher) { + return MatcherList(Matcher); +} + +template <typename T> +static MatcherList +outvalueToMatcherList(const T& PolyMatcher, typename T::ReturnTypes* = NULL) { + MatcherList Matchers; + mergePolyMatchers(PolyMatcher, &Matchers, typename T::ReturnTypes()); + return Matchers; +} + /// \brief 0-arg marshaller function. template <typename ReturnType> -DynTypedMatcher *matcherMarshall0(ReturnType (*Func)(), StringRef MatcherName, - const SourceRange &NameRange, - ArrayRef<ParserValue> Args, - Diagnostics *Error) { +static MatcherList matcherMarshall0(ReturnType (*Func)(), + StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef<ParserValue> Args, + Diagnostics *Error) { CHECK_ARG_COUNT(0); - return Func().clone(); + return outvalueToMatcherList(Func()); } /// \brief 1-arg marshaller function. template <typename ReturnType, typename ArgType1> -DynTypedMatcher *matcherMarshall1(ReturnType (*Func)(ArgType1), - StringRef MatcherName, - const SourceRange &NameRange, - ArrayRef<ParserValue> Args, - Diagnostics *Error) { +static MatcherList matcherMarshall1(ReturnType (*Func)(ArgType1), + StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef<ParserValue> Args, + Diagnostics *Error) { CHECK_ARG_COUNT(1); CHECK_ARG_TYPE(0, ArgType1); - return Func(ArgTypeTraits<ArgType1>::get(Args[0].Value)).clone(); + return outvalueToMatcherList( + Func(ArgTypeTraits<ArgType1>::get(Args[0].Value))); } /// \brief 2-arg marshaller function. template <typename ReturnType, typename ArgType1, typename ArgType2> -DynTypedMatcher *matcherMarshall2(ReturnType (*Func)(ArgType1, ArgType2), - StringRef MatcherName, - const SourceRange &NameRange, - ArrayRef<ParserValue> Args, - Diagnostics *Error) { +static MatcherList matcherMarshall2(ReturnType (*Func)(ArgType1, ArgType2), + StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef<ParserValue> Args, + Diagnostics *Error) { CHECK_ARG_COUNT(2); CHECK_ARG_TYPE(0, ArgType1); CHECK_ARG_TYPE(1, ArgType2); - return Func(ArgTypeTraits<ArgType1>::get(Args[0].Value), - ArgTypeTraits<ArgType2>::get(Args[1].Value)).clone(); + return outvalueToMatcherList( + Func(ArgTypeTraits<ArgType1>::get(Args[0].Value), + ArgTypeTraits<ArgType2>::get(Args[1].Value))); } #undef CHECK_ARG_COUNT @@ -202,10 +241,10 @@ DynTypedMatcher *matcherMarshall2(ReturnType (*Func)(ArgType1, ArgType2), /// \brief Variadic marshaller function. template <typename BaseType, typename DerivedType> -DynTypedMatcher *VariadicMatcherCreateCallback(StringRef MatcherName, - const SourceRange &NameRange, - ArrayRef<ParserValue> Args, - Diagnostics *Error) { +MatcherList VariadicMatcherCreateCallback(StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef<ParserValue> Args, + Diagnostics *Error) { typedef ast_matchers::internal::Matcher<DerivedType> DerivedMatcherType; DerivedMatcherType **InnerArgs = new DerivedMatcherType *[Args.size()](); @@ -223,10 +262,10 @@ DynTypedMatcher *VariadicMatcherCreateCallback(StringRef MatcherName, InnerArgs[i] = new DerivedMatcherType(DerivedTraits::get(Value)); } - DynTypedMatcher *Out = NULL; + MatcherList Out; if (!HasError) { Out = ast_matchers::internal::makeDynCastAllOfComposite<BaseType>( - ArrayRef<const DerivedMatcherType *>(InnerArgs, Args.size())).clone(); + ArrayRef<const DerivedMatcherType *>(InnerArgs, Args.size())); } for (size_t i = 0, e = Args.size(); i != e; ++i) { @@ -264,7 +303,7 @@ MatcherCreateCallback *makeMatcherAutoMarshall(ReturnType (*Func)(ArgType1, ReturnType (*)(ArgType1, ArgType2)>(matcherMarshall2, Func, MatcherName); } -/// \brief Variadic overload. +/// \brief Variadic overloads. template <typename MatcherType> MatcherCreateCallback *makeMatcherAutoMarshall( ast_matchers::internal::VariadicAllOfMatcher<MatcherType> Func, diff --git a/lib/ASTMatchers/Dynamic/Parser.cpp b/lib/ASTMatchers/Dynamic/Parser.cpp index eff50f4061c..fc09a30ddd9 100644 --- a/lib/ASTMatchers/Dynamic/Parser.cpp +++ b/lib/ASTMatchers/Dynamic/Parser.cpp @@ -315,15 +315,15 @@ bool Parser::parseMatcherExpressionImpl(VariantValue *Value) { // Merge the start and end infos. SourceRange MatcherRange = NameToken.Range; MatcherRange.End = EndToken.Range.End; - DynTypedMatcher *Result = S->actOnMatcherExpression( + MatcherList Result = S->actOnMatcherExpression( NameToken.Text, MatcherRange, BindID, Args, Error); - if (Result == NULL) { + if (Result.empty()) { Error->pushErrorFrame(NameToken.Range, Error->ET_ParserMatcherFailure) << NameToken.Text; return false; } - Value->takeMatcher(Result); + *Value = Result; return true; } @@ -367,11 +367,11 @@ Parser::Parser(CodeTokenizer *Tokenizer, Sema *S, class RegistrySema : public Parser::Sema { public: virtual ~RegistrySema() {} - DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName, - const SourceRange &NameRange, - StringRef BindID, - ArrayRef<ParserValue> Args, - Diagnostics *Error) { + MatcherList actOnMatcherExpression(StringRef MatcherName, + const SourceRange &NameRange, + StringRef BindID, + ArrayRef<ParserValue> Args, + Diagnostics *Error) { if (BindID.empty()) { return Registry::constructMatcher(MatcherName, NameRange, Args, Error); } else { @@ -411,11 +411,16 @@ DynTypedMatcher *Parser::parseMatcherExpression(StringRef Code, VariantValue Value; if (!parseExpression(Code, S, &Value, Error)) return NULL; - if (!Value.isMatcher()) { + if (!Value.isMatchers()) { Error->pushErrorFrame(SourceRange(), Error->ET_ParserNotAMatcher); return NULL; } - return Value.getMatcher().clone(); + if (Value.getMatchers().matchers().size() != 1) { + Error->pushErrorFrame(SourceRange(), Error->ET_ParserOverloadedType) + << Value.getTypeAsString(); + return NULL; + } + return Value.getMatchers().matchers()[0]->clone(); } } // namespace dynamic diff --git a/lib/ASTMatchers/Dynamic/Registry.cpp b/lib/ASTMatchers/Dynamic/Registry.cpp index 5f6b35df696..c887f3da603 100644 --- a/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/lib/ASTMatchers/Dynamic/Registry.cpp @@ -68,7 +68,6 @@ RegistryMaps::RegistryMaps() { // forField // withInitializer // isWritten - // isImplicit // // Type traversal: // hasElementType @@ -87,19 +86,6 @@ RegistryMaps::RegistryMaps() { // references // thisPointerType // - // Polymorphic matchers: - // anything - // hasAnyArgument - // isTemplateInstantiation - // isExplicitTemplateSpecialization - // isDefinition - // hasOperatorName - // hasOverloadedOperatorName - // hasCondition - // hasBody - // argumentCountIs - // hasArgument - // // Polymorphic + argument overload: // unless // eachOf @@ -123,6 +109,8 @@ RegistryMaps::RegistryMaps() { REGISTER_MATCHER(accessSpecDecl); REGISTER_MATCHER(alignOfExpr); + REGISTER_MATCHER(anything); + REGISTER_MATCHER(argumentCountIs); REGISTER_MATCHER(arraySubscriptExpr); REGISTER_MATCHER(arrayType); REGISTER_MATCHER(asString); @@ -175,12 +163,16 @@ RegistryMaps::RegistryMaps() { REGISTER_MATCHER(functionType); REGISTER_MATCHER(functionalCastExpr); REGISTER_MATCHER(gotoStmt); + REGISTER_MATCHER(hasAnyArgument); REGISTER_MATCHER(hasAnyParameter); REGISTER_MATCHER(hasAnySubstatement); REGISTER_MATCHER(hasAnyUsingShadowDecl); + REGISTER_MATCHER(hasArgument); REGISTER_MATCHER(hasArgumentOfType); REGISTER_MATCHER(hasBase); + REGISTER_MATCHER(hasBody); REGISTER_MATCHER(hasCanonicalType); + REGISTER_MATCHER(hasCondition); REGISTER_MATCHER(hasConditionVariableStatement); REGISTER_MATCHER(hasDeclContext); REGISTER_MATCHER(hasDestinationType); @@ -196,6 +188,8 @@ RegistryMaps::RegistryMaps() { REGISTER_MATCHER(hasMethod); REGISTER_MATCHER(hasName); REGISTER_MATCHER(hasObjectExpression); + REGISTER_MATCHER(hasOperatorName); + REGISTER_MATCHER(hasOverloadedOperatorName); REGISTER_MATCHER(hasParameter); REGISTER_MATCHER(hasQualifier); REGISTER_MATCHER(hasRHS); @@ -216,6 +210,8 @@ RegistryMaps::RegistryMaps() { REGISTER_MATCHER(integerLiteral); REGISTER_MATCHER(isArrow); REGISTER_MATCHER(isConstQualified); + REGISTER_MATCHER(isDefinition); + REGISTER_MATCHER(isExplicitTemplateSpecialization); REGISTER_MATCHER(isExternC); REGISTER_MATCHER(isImplicit); REGISTER_MATCHER(isInteger); @@ -223,6 +219,7 @@ RegistryMaps::RegistryMaps() { REGISTER_MATCHER(isPrivate); REGISTER_MATCHER(isProtected); REGISTER_MATCHER(isPublic); + REGISTER_MATCHER(isTemplateInstantiation); REGISTER_MATCHER(isVirtual); REGISTER_MATCHER(lValueReferenceType); REGISTER_MATCHER(labelStmt); @@ -300,36 +297,39 @@ static llvm::ManagedStatic<RegistryMaps> RegistryData; } // anonymous namespace // static -DynTypedMatcher *Registry::constructMatcher(StringRef MatcherName, - const SourceRange &NameRange, - ArrayRef<ParserValue> Args, - Diagnostics *Error) { +MatcherList Registry::constructMatcher(StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef<ParserValue> Args, + Diagnostics *Error) { ConstructorMap::const_iterator it = RegistryData->constructors().find(MatcherName); if (it == RegistryData->constructors().end()) { Error->pushErrorFrame(NameRange, Error->ET_RegistryNotFound) << MatcherName; - return NULL; + return MatcherList(); } return it->second->run(NameRange, Args, Error); } // static -DynTypedMatcher *Registry::constructBoundMatcher(StringRef MatcherName, - const SourceRange &NameRange, - StringRef BindID, - ArrayRef<ParserValue> Args, - Diagnostics *Error) { - OwningPtr<DynTypedMatcher> Out( - constructMatcher(MatcherName, NameRange, Args, Error)); - if (!Out) return NULL; - DynTypedMatcher *Bound = Out->tryBind(BindID); - if (!Bound) { - Error->pushErrorFrame(NameRange, Error->ET_RegistryNotBindable); - return NULL; +MatcherList Registry::constructBoundMatcher(StringRef MatcherName, + const SourceRange &NameRange, + StringRef BindID, + ArrayRef<ParserValue> Args, + Diagnostics *Error) { + MatcherList Out = constructMatcher(MatcherName, NameRange, Args, Error); + if (Out.empty()) return Out; + + ArrayRef<const DynTypedMatcher*> Matchers = Out.matchers(); + if (Matchers.size() == 1) { + OwningPtr<DynTypedMatcher> Bound(Matchers[0]->tryBind(BindID)); + if (Bound) { + return *Bound; + } } - return Bound; + Error->pushErrorFrame(NameRange, Error->ET_RegistryNotBindable); + return MatcherList(); } } // namespace dynamic diff --git a/lib/ASTMatchers/Dynamic/VariantValue.cpp b/lib/ASTMatchers/Dynamic/VariantValue.cpp index 912858f5485..79c5c8e23a6 100644 --- a/lib/ASTMatchers/Dynamic/VariantValue.cpp +++ b/lib/ASTMatchers/Dynamic/VariantValue.cpp @@ -20,6 +20,48 @@ namespace clang { namespace ast_matchers { namespace dynamic { +MatcherList::MatcherList() : List() {} + +MatcherList::MatcherList(const DynTypedMatcher &Matcher) + : List(1, Matcher.clone()) {} + +MatcherList::MatcherList(const MatcherList& Other) { + *this = Other; +} + +MatcherList::~MatcherList() { + reset(); +} + +MatcherList &MatcherList::operator=(const MatcherList &Other) { + if (this == &Other) return *this; + reset(); + for (size_t i = 0, e = Other.List.size(); i != e; ++i) { + List.push_back(Other.List[i]->clone()); + } + return *this; +} + +void MatcherList::add(const DynTypedMatcher &Matcher) { + List.push_back(Matcher.clone()); +} + +void MatcherList::reset() { + for (size_t i = 0, e = List.size(); i != e; ++i) { + delete List[i]; + } + List.resize(0); +} + +std::string MatcherList::getTypeAsString() const { + std::string Inner; + for (size_t I = 0, E = List.size(); I != E; ++I) { + if (I != 0) Inner += "|"; + Inner += List[I]->getSupportedKind().asStringRef(); + } + return (Twine("Matcher<") + Inner + ">").str(); +} + VariantValue::VariantValue(const VariantValue &Other) : Type(VT_Nothing) { *this = Other; } @@ -33,7 +75,11 @@ VariantValue::VariantValue(const std::string &String) : Type(VT_Nothing) { } VariantValue::VariantValue(const DynTypedMatcher &Matcher) : Type(VT_Nothing) { - setMatcher(Matcher); + setMatchers(MatcherList(Matcher)); +} + +VariantValue::VariantValue(const MatcherList &Matchers) : Type(VT_Nothing) { + setMatchers(Matchers); } VariantValue::~VariantValue() { reset(); } @@ -48,8 +94,8 @@ VariantValue &VariantValue::operator=(const VariantValue &Other) { case VT_String: setString(Other.getString()); break; - case VT_Matcher: - setMatcher(Other.getMatcher()); + case VT_Matchers: + setMatchers(Other.getMatchers()); break; case VT_Nothing: Type = VT_Nothing; @@ -63,8 +109,8 @@ void VariantValue::reset() { case VT_String: delete Value.String; break; - case VT_Matcher: - delete Value.Matcher; + case VT_Matchers: + delete Value.Matchers; break; // Cases that do nothing. case VT_Unsigned: @@ -104,33 +150,25 @@ void VariantValue::setString(const std::string &NewValue) { Value.String = new std::string(NewValue); } -bool VariantValue::isMatcher() const { - return Type == VT_Matcher; +bool VariantValue::isMatchers() const { + return Type == VT_Matchers; } -const DynTypedMatcher &VariantValue::getMatcher() const { - assert(isMatcher()); - return *Value.Matcher; -} - -void VariantValue::setMatcher(const DynTypedMatcher &NewValue) { - reset(); - Type = VT_Matcher; - Value.Matcher = NewValue.clone(); +const MatcherList &VariantValue::getMatchers() const { + assert(isMatchers()); + return *Value.Matchers; } -void VariantValue::takeMatcher(DynTypedMatcher *NewValue) { +void VariantValue::setMatchers(const MatcherList &NewValue) { reset(); - Type = VT_Matcher; - Value.Matcher = NewValue; + Type = VT_Matchers; + Value.Matchers = new MatcherList(NewValue); } std::string VariantValue::getTypeAsString() const { switch (Type) { case VT_String: return "String"; - case VT_Matcher: - return (Twine("Matcher<") + getMatcher().getSupportedKind().asStringRef() + - ">").str(); + case VT_Matchers: return getMatchers().getTypeAsString(); case VT_Unsigned: return "Unsigned"; case VT_Nothing: return "Nothing"; } diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp index 839c447061f..8df0274504e 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -2250,10 +2250,9 @@ TEST(AstMatcherPMacro, Works) { } AST_POLYMORPHIC_MATCHER_P( - polymorphicHas, internal::Matcher<Decl>, AMatcher) { - TOOLING_COMPILE_ASSERT((llvm::is_same<NodeType, Decl>::value) || - (llvm::is_same<NodeType, Stmt>::value), - assert_node_type_is_accessible); + polymorphicHas, + AST_POLYMORPHIC_SUPPORTED_TYPES_2(Decl, Stmt), + internal::Matcher<Decl>, AMatcher) { return Finder->matchesChildOf( Node, AMatcher, Builder, ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses, diff --git a/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/unittests/ASTMatchers/Dynamic/ParserTest.cpp index d7973c957f1..e6b04e0f762 100644 --- a/unittests/ASTMatchers/Dynamic/ParserTest.cpp +++ b/unittests/ASTMatchers/Dynamic/ParserTest.cpp @@ -52,6 +52,7 @@ public: virtual ast_type_traits::ASTNodeKind getSupportedKind() const { return ast_type_traits::ASTNodeKind(); } + private: uint64_t ID; std::string BoundID; @@ -75,15 +76,16 @@ public: Errors.push_back(Error.ToStringFull()); } - DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName, - const SourceRange &NameRange, - StringRef BindID, - ArrayRef<ParserValue> Args, - Diagnostics *Error) { + MatcherList actOnMatcherExpression(StringRef MatcherName, + const SourceRange &NameRange, + StringRef BindID, + ArrayRef<ParserValue> Args, + Diagnostics *Error) { MatcherInfo ToStore = { MatcherName, NameRange, Args, BindID }; Matchers.push_back(ToStore); DummyDynTypedMatcher Matcher(ExpectedMatchers[MatcherName]); - return Matcher.tryBind(BindID); + OwningPtr<DynTypedMatcher> Out(Matcher.tryBind(BindID)); + return *Out; } struct MatcherInfo { @@ -146,9 +148,9 @@ TEST(ParserTest, ParseMatcher) { } EXPECT_EQ(1ULL, Sema.Values.size()); - EXPECT_EQ(ExpectedFoo, Sema.Values[0].getMatcher().getID()); - EXPECT_EQ("Yo!", static_cast<const DummyDynTypedMatcher &>( - Sema.Values[0].getMatcher()).boundID()); + EXPECT_EQ(ExpectedFoo, Sema.Values[0].getMatchers().matchers()[0]->getID()); + EXPECT_EQ("Yo!", static_cast<const DummyDynTypedMatcher *>( + Sema.Values[0].getMatchers().matchers()[0])->boundID()); EXPECT_EQ(3ULL, Sema.Matchers.size()); const MockSema::MatcherInfo Bar = Sema.Matchers[0]; @@ -167,8 +169,10 @@ TEST(ParserTest, ParseMatcher) { EXPECT_EQ("Foo", Foo.MatcherName); EXPECT_TRUE(matchesRange(Foo.NameRange, 1, 2, 2, 12)); EXPECT_EQ(2ULL, Foo.Args.size()); - EXPECT_EQ(ExpectedBar, Foo.Args[0].Value.getMatcher().getID()); - EXPECT_EQ(ExpectedBaz, Foo.Args[1].Value.getMatcher().getID()); + EXPECT_EQ(ExpectedBar, + Foo.Args[0].Value.getMatchers().matchers()[0]->getID()); + EXPECT_EQ(ExpectedBaz, + Foo.Args[1].Value.getMatchers().matchers()[0]->getID()); EXPECT_EQ("Yo!", Foo.BoundID); } @@ -176,11 +180,14 @@ using ast_matchers::internal::Matcher; TEST(ParserTest, FullParserTest) { OwningPtr<DynTypedMatcher> VarDecl(Parser::parseMatcherExpression( - "varDecl(hasInitializer(binaryOperator(hasLHS(integerLiteral()))))", + "varDecl(hasInitializer(binaryOperator(hasLHS(integerLiteral())," + " hasOperatorName(\"+\"))))", NULL)); Matcher<Decl> M = Matcher<Decl>::constructFrom(*VarDecl); EXPECT_TRUE(matches("int x = 1 + false;", M)); EXPECT_FALSE(matches("int x = true + 1;", M)); + EXPECT_FALSE(matches("int x = 1 - false;", M)); + EXPECT_FALSE(matches("int x = true - 1;", M)); OwningPtr<DynTypedMatcher> HasParameter(Parser::parseMatcherExpression( "functionDecl(hasParameter(1, hasName(\"x\")))", NULL)); @@ -242,6 +249,9 @@ TEST(ParserTest, Errors) { EXPECT_EQ("1:1: Error building matcher isArrow.\n" "1:1: Matcher does not support binding.", ParseWithError("isArrow().bind(\"foo\")")); + EXPECT_EQ("Input value has unresolved overloaded type: " + "Matcher<DoStmt|ForStmt|WhileStmt>", + ParseMatcherWithError("hasBody(stmt())")); } } // end anonymous namespace diff --git a/unittests/ASTMatchers/Dynamic/RegistryTest.cpp b/unittests/ASTMatchers/Dynamic/RegistryTest.cpp index fd6eaef2ea8..357a371e1e9 100644 --- a/unittests/ASTMatchers/Dynamic/RegistryTest.cpp +++ b/unittests/ASTMatchers/Dynamic/RegistryTest.cpp @@ -38,25 +38,23 @@ public: template <class T> Matcher<T> constructMatcher(StringRef MatcherName, Diagnostics *Error) { - OwningPtr<DynTypedMatcher> Out( - Registry::constructMatcher(MatcherName, SourceRange(), Args(), Error)); - return Matcher<T>::constructFrom(*Out); + return Registry::constructMatcher(MatcherName, SourceRange(), Args(), Error) + .getTypedMatcher<T>(); } template <class T> Matcher<T> constructMatcher(StringRef MatcherName, const VariantValue &Arg1, Diagnostics *Error) { - OwningPtr<DynTypedMatcher> Out(Registry::constructMatcher( - MatcherName, SourceRange(), Args(Arg1), Error)); - return Matcher<T>::constructFrom(*Out); + return Registry::constructMatcher(MatcherName, SourceRange(), Args(Arg1), + Error).getTypedMatcher<T>(); } template <class T> Matcher<T> constructMatcher(StringRef MatcherName, const VariantValue &Arg1, const VariantValue &Arg2, Diagnostics *Error) { - OwningPtr<DynTypedMatcher> Out(Registry::constructMatcher( - MatcherName, SourceRange(), Args(Arg1, Arg2), Error)); - return Matcher<T>::constructFrom(*Out); + return Registry::constructMatcher(MatcherName, SourceRange(), + Args(Arg1, Arg2), Error) + .getTypedMatcher<T>(); } }; @@ -115,34 +113,57 @@ TEST_F(RegistryTest, ConstructWithMatcherArgs) { EXPECT_FALSE(matches("void f(int x, int a);", HasParameter)); } +TEST_F(RegistryTest, PolymorphicMatchers) { + const MatcherList IsDefinition = + Registry::constructMatcher("isDefinition", SourceRange(), Args(), NULL); + Matcher<Decl> Var = constructMatcher<Decl>("varDecl", IsDefinition, NULL); + Matcher<Decl> Class = + constructMatcher<Decl>("recordDecl", IsDefinition, NULL); + Matcher<Decl> Func = + constructMatcher<Decl>("functionDecl", IsDefinition, NULL); + EXPECT_TRUE(matches("int a;", Var)); + EXPECT_FALSE(matches("extern int a;", Var)); + EXPECT_TRUE(matches("class A {};", Class)); + EXPECT_FALSE(matches("class A;", Class)); + EXPECT_TRUE(matches("void f(){};", Func)); + EXPECT_FALSE(matches("void f();", Func)); + + Matcher<Decl> Anything = constructMatcher<Decl>("anything", NULL); + Matcher<Decl> RecordDecl = + constructMatcher<Decl>("recordDecl", Anything, NULL); + + EXPECT_TRUE(matches("int a;", Anything)); + EXPECT_TRUE(matches("class A {};", Anything)); + EXPECT_TRUE(matches("void f(){};", Anything)); + EXPECT_FALSE(matches("int a;", RecordDecl)); + EXPECT_TRUE(matches("class A {};", RecordDecl)); + EXPECT_FALSE(matches("void f(){};", RecordDecl)); +} + TEST_F(RegistryTest, Errors) { // Incorrect argument count. OwningPtr<Diagnostics> Error(new Diagnostics()); - EXPECT_TRUE(NULL == - Registry::constructMatcher("hasInitializer", SourceRange(), - Args(), Error.get())); + EXPECT_TRUE(Registry::constructMatcher("hasInitializer", SourceRange(), + Args(), Error.get()).empty()); EXPECT_EQ("Incorrect argument count. (Expected = 1) != (Actual = 0)", Error->ToString()); Error.reset(new Diagnostics()); - EXPECT_TRUE(NULL == - Registry::constructMatcher("isArrow", SourceRange(), - Args(std::string()), Error.get())); + EXPECT_TRUE(Registry::constructMatcher( + "isArrow", SourceRange(), Args(std::string()), Error.get()).empty()); EXPECT_EQ("Incorrect argument count. (Expected = 0) != (Actual = 1)", Error->ToString()); // Bad argument type Error.reset(new Diagnostics()); - EXPECT_TRUE(NULL == - Registry::constructMatcher("ofClass", SourceRange(), - Args(std::string()), Error.get())); + EXPECT_TRUE(Registry::constructMatcher( + "ofClass", SourceRange(), Args(std::string()), Error.get()).empty()); EXPECT_EQ("Incorrect type for arg 1. (Expected = Matcher<CXXRecordDecl>) != " "(Actual = String)", Error->ToString()); Error.reset(new Diagnostics()); - EXPECT_TRUE(NULL == - Registry::constructMatcher( - "recordDecl", SourceRange(), - Args(recordDecl(), parameterCountIs(3)), Error.get())); + EXPECT_TRUE(Registry::constructMatcher( + "recordDecl", SourceRange(), Args(recordDecl(), parameterCountIs(3)), + Error.get()).empty()); EXPECT_EQ("Incorrect type for arg 2. (Expected = Matcher<CXXRecordDecl>) != " "(Actual = Matcher<FunctionDecl>)", Error->ToString()); diff --git a/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp b/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp index c9416728e63..625f70bd465 100644 --- a/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp +++ b/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp @@ -27,9 +27,9 @@ TEST(VariantValueTest, Unsigned) { EXPECT_EQ(kUnsigned, Value.getUnsigned()); EXPECT_FALSE(Value.isString()); - EXPECT_FALSE(Value.isMatcher()); - EXPECT_FALSE(Value.hasTypedMatcher<clang::Decl>()); - EXPECT_FALSE(Value.hasTypedMatcher<clang::UnaryOperator>()); + EXPECT_FALSE(Value.isMatchers()); + EXPECT_FALSE(Value.hasTypedMatcher<Decl>()); + EXPECT_FALSE(Value.hasTypedMatcher<UnaryOperator>()); } TEST(VariantValueTest, String) { @@ -41,9 +41,7 @@ TEST(VariantValueTest, String) { EXPECT_EQ("String", Value.getTypeAsString()); EXPECT_FALSE(Value.isUnsigned()); - EXPECT_FALSE(Value.isMatcher()); - EXPECT_FALSE(Value.hasTypedMatcher<clang::Decl>()); - EXPECT_FALSE(Value.hasTypedMatcher<clang::UnaryOperator>()); + EXPECT_FALSE(Value.isMatchers()); } TEST(VariantValueTest, DynTypedMatcher) { @@ -52,25 +50,25 @@ TEST(VariantValueTest, DynTypedMatcher) { EXPECT_FALSE(Value.isUnsigned()); EXPECT_FALSE(Value.isString()); - EXPECT_TRUE(Value.isMatcher()); - EXPECT_FALSE(Value.hasTypedMatcher<clang::Decl>()); - EXPECT_TRUE(Value.hasTypedMatcher<clang::UnaryOperator>()); + EXPECT_TRUE(Value.isMatchers()); + EXPECT_FALSE(Value.hasTypedMatcher<Decl>()); + EXPECT_TRUE(Value.hasTypedMatcher<UnaryOperator>()); EXPECT_EQ("Matcher<Stmt>", Value.getTypeAsString()); // Can only convert to compatible matchers. Value = recordDecl(); - EXPECT_TRUE(Value.isMatcher()); - EXPECT_TRUE(Value.hasTypedMatcher<clang::Decl>()); - EXPECT_FALSE(Value.hasTypedMatcher<clang::UnaryOperator>()); + EXPECT_TRUE(Value.isMatchers()); + EXPECT_TRUE(Value.hasTypedMatcher<Decl>()); + EXPECT_FALSE(Value.hasTypedMatcher<UnaryOperator>()); EXPECT_EQ("Matcher<Decl>", Value.getTypeAsString()); Value = ignoringImpCasts(expr()); - EXPECT_TRUE(Value.isMatcher()); - EXPECT_FALSE(Value.hasTypedMatcher<clang::Decl>()); - EXPECT_FALSE(Value.hasTypedMatcher<clang::Stmt>()); - EXPECT_TRUE(Value.hasTypedMatcher<clang::Expr>()); - EXPECT_TRUE(Value.hasTypedMatcher<clang::IntegerLiteral>()); - EXPECT_FALSE(Value.hasTypedMatcher<clang::GotoStmt>()); + EXPECT_TRUE(Value.isMatchers()); + EXPECT_FALSE(Value.hasTypedMatcher<Decl>()); + EXPECT_FALSE(Value.hasTypedMatcher<Stmt>()); + EXPECT_TRUE(Value.hasTypedMatcher<Expr>()); + EXPECT_TRUE(Value.hasTypedMatcher<IntegerLiteral>()); + EXPECT_FALSE(Value.hasTypedMatcher<GotoStmt>()); EXPECT_EQ("Matcher<Expr>", Value.getTypeAsString()); } @@ -79,31 +77,31 @@ TEST(VariantValueTest, Assignment) { EXPECT_TRUE(Value.isString()); EXPECT_EQ("A", Value.getString()); EXPECT_FALSE(Value.isUnsigned()); - EXPECT_FALSE(Value.isMatcher()); + EXPECT_FALSE(Value.isMatchers()); EXPECT_EQ("String", Value.getTypeAsString()); Value = recordDecl(); EXPECT_FALSE(Value.isUnsigned()); EXPECT_FALSE(Value.isString()); - EXPECT_TRUE(Value.isMatcher()); - EXPECT_TRUE(Value.hasTypedMatcher<clang::Decl>()); - EXPECT_FALSE(Value.hasTypedMatcher<clang::UnaryOperator>()); + EXPECT_TRUE(Value.isMatchers()); + EXPECT_TRUE(Value.hasTypedMatcher<Decl>()); + EXPECT_FALSE(Value.hasTypedMatcher<UnaryOperator>()); EXPECT_EQ("Matcher<Decl>", Value.getTypeAsString()); Value = 17; EXPECT_TRUE(Value.isUnsigned()); EXPECT_EQ(17U, Value.getUnsigned()); - EXPECT_FALSE(Value.isMatcher()); + EXPECT_FALSE(Value.isMatchers()); EXPECT_FALSE(Value.isString()); Value = VariantValue(); EXPECT_FALSE(Value.isUnsigned()); EXPECT_FALSE(Value.isString()); - EXPECT_FALSE(Value.isMatcher()); + EXPECT_FALSE(Value.isMatchers()); EXPECT_EQ("Nothing", Value.getTypeAsString()); } -TEST(GenericValueTest, Matcher) { +TEST(VariantValueTest, Matcher) { EXPECT_TRUE(matches("class X {};", VariantValue(recordDecl(hasName("X"))) .getTypedMatcher<Decl>())); EXPECT_TRUE( @@ -117,13 +115,15 @@ TEST(GenericValueTest, Matcher) { // do this test when building with MSVC because its debug C runtime prints the // assertion failure message as a wide string, which gtest doesn't understand. EXPECT_DEATH(VariantValue(varDecl()).getTypedMatcher<Stmt>(), - "canConstructFrom"); + "hasTypedMatcher"); #endif EXPECT_FALSE( matches("int x;", VariantValue(functionDecl()).getTypedMatcher<Decl>())); - EXPECT_FALSE(matches("int foo() { return 1 + 1; }", - VariantValue(declRefExpr()).getTypedMatcher<Stmt>())); + EXPECT_FALSE( + matches("int foo() { return 1 + 1; }", + + VariantValue(declRefExpr()).getTypedMatcher<Stmt>())); } } // end anonymous namespace -- GitLab