From: Daniel Jasper Date: Wed, 17 Oct 2012 08:52:59 +0000 (+0000) Subject: First version of matchers for Types and TypeLocs. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ce62007526cdf718faed10df5e9fc7c3cd160cde;p=clang First version of matchers for Types and TypeLocs. Review: http://llvm-reviews.chandlerc.com/D47 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@166094 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/ASTMatchers/ASTMatchFinder.h b/include/clang/ASTMatchers/ASTMatchFinder.h index 64a86d7ebe..1cf2036640 100644 --- a/include/clang/ASTMatchers/ASTMatchFinder.h +++ b/include/clang/ASTMatchers/ASTMatchFinder.h @@ -116,6 +116,8 @@ public: MatchCallback *Action); void addMatcher(const NestedNameSpecifierLocMatcher &NodeMatch, MatchCallback *Action); + void addMatcher(const TypeLocMatcher &NodeMatch, + MatchCallback *Action); /// @} /// \brief Creates a clang ASTConsumer that finds all matches. diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h index 770ec779b1..1550fa2d75 100644 --- a/include/clang/ASTMatchers/ASTMatchers.h +++ b/include/clang/ASTMatchers/ASTMatchers.h @@ -108,8 +108,9 @@ internal::Matcher id(const std::string &ID, /// hierarchy. /// @{ typedef internal::Matcher DeclarationMatcher; -typedef internal::Matcher TypeMatcher; typedef internal::Matcher StatementMatcher; +typedef internal::Matcher TypeMatcher; +typedef internal::Matcher TypeLocMatcher; typedef internal::Matcher NestedNameSpecifierMatcher; typedef internal::Matcher NestedNameSpecifierLocMatcher; /// @} @@ -2438,6 +2439,282 @@ isExplicitTemplateSpecialization() { internal::IsExplicitTemplateSpecializationMatcher>(); } +/// \brief Matches \c QualTypes in the clang AST. +const internal::VariadicAllOfMatcher qualType; + +/// \brief Matches \c Types in the clang AST. +const internal::VariadicDynCastAllOfMatcher type; + +/// \brief Matches \c TypeLocs in the clang AST. +const internal::VariadicDynCastAllOfMatcher typeLoc; + +/// \brief Matches \c TypeLocs for which the given inner +/// QualType-matcher matches. +inline internal::BindableMatcher loc( + const internal::Matcher &InnerMatcher) { + return internal::BindableMatcher( + new internal::TypeLocTypeMatcher(InnerMatcher)); +} + +/// \brief Matches builtin Types. +/// +/// Given +/// \code +/// struct A {}; +/// A a; +/// int b; +/// float c; +/// bool d; +/// \endcode +/// builtinType() +/// matches "int b", "float c" and "bool d" +AST_TYPE_MATCHER(BuiltinType, builtinType); + +/// \brief Matches all kinds of arrays. +/// +/// Given +/// \code +/// int a[] = { 2, 3 }; +/// int b[4]; +/// void f() { int c[a[0]]; } +/// \endcode +/// arrayType() +/// matches "int a[]", "int b[4]" and "int c[a[0]]"; +AST_TYPE_MATCHER(ArrayType, arrayType); + +/// \brief Matches C99 complex types. +/// +/// Given +/// \code +/// _Complex float f; +/// \endcode +/// complexType() +/// matches "_Complex float f" +AST_TYPE_MATCHER(ComplexType, complexType); + +/// \brief Matches arrays and C99 complex types that have a specific element +/// type. +/// +/// Given +/// \code +/// struct A {}; +/// A a[7]; +/// int b[7]; +/// \endcode +/// arrayType(hasElementType(builtinType())) +/// matches "int b[7]" +/// +/// Usable as: Matcher, Matcher +AST_TYPELOC_TRAVERSE_MATCHER(hasElementType, getElement); + +/// \brief Matches C arrays with a specified constant size. +/// +/// Given +/// \code +/// void() { +/// int a[2]; +/// int b[] = { 2, 3 }; +/// int c[b[0]]; +/// } +/// \endcode +/// constantArrayType() +/// matches "int a[2]" +AST_TYPE_MATCHER(ConstantArrayType, constantArrayType); + +/// \brief Matches \c ConstantArrayType nodes that have the specified size. +/// +/// Given +/// \code +/// int a[42]; +/// int b[2 * 21]; +/// int c[41], d[43]; +/// \endcode +/// constantArrayType(hasSize(42)) +/// matches "int a[42]" and "int b[2 * 21]" +AST_MATCHER_P(ConstantArrayType, hasSize, unsigned, N) { + return Node.getSize() == N; +} + +/// \brief Matches C++ arrays whose size is a value-dependent expression. +/// +/// Given +/// \code +/// template +/// class array { +/// T data[Size]; +/// }; +/// \endcode +/// dependentSizedArrayType +/// matches "T data[Size]" +AST_TYPE_MATCHER(DependentSizedArrayType, dependentSizedArrayType); + +/// \brief Matches C arrays with unspecified size. +/// +/// Given +/// \code +/// int a[] = { 2, 3 }; +/// int b[42]; +/// void f(int c[]) { int d[a[0]]; }; +/// \endcode +/// incompleteArrayType() +/// matches "int a[]" and "int c[]" +AST_TYPE_MATCHER(IncompleteArrayType, incompleteArrayType); + +/// \brief Matches C arrays with a specified size that is not an +/// integer-constant-expression. +/// +/// Given +/// \code +/// void f() { +/// int a[] = { 2, 3 } +/// int b[42]; +/// int c[a[0]]; +/// \endcode +/// variableArrayType() +/// matches "int c[a[0]]" +AST_TYPE_MATCHER(VariableArrayType, variableArrayType); + +/// \brief Matches \c VariableArrayType nodes that have a specific size +/// expression. +/// +/// Given +/// \code +/// void f(int b) { +/// int a[b]; +/// } +/// \endcode +/// variableArrayType(hasSizeExpr(ignoringImpCasts(declRefExpr(to( +/// varDecl(hasName("b"))))))) +/// matches "int a[b]" +AST_MATCHER_P(VariableArrayType, hasSizeExpr, + internal::Matcher, InnerMatcher) { + return InnerMatcher.matches(*Node.getSizeExpr(), Finder, Builder); +} + +/// \brief Matches atomic types. +/// +/// Given +/// \code +/// _Atomic(int) i; +/// \endcode +/// atomicType() +/// matches "_Atomic(int) i" +AST_TYPE_MATCHER(AtomicType, atomicType); + +/// \brief Matches atomic types with a specific value type. +/// +/// Given +/// \code +/// _Atomic(int) i; +/// _Atomic(float) f; +/// \endcode +/// atomicType(hasValueType(isInteger())) +/// matches "_Atomic(int) i" +/// +/// Usable as: Matcher +AST_TYPELOC_TRAVERSE_MATCHER(hasValueType, getValue); + +/// \brief Matches types nodes representing C++11 auto types. +/// +/// Given: +/// \code +/// auto n = 4; +/// int v[] = { 2, 3 } +/// for (auto i : v) { } +/// \endcode +/// autoType() +/// matches "auto n" and "auto i" +AST_TYPE_MATCHER(AutoType, autoType); + +/// \brief Matches \c AutoType nodes where the deduced type is a specific type. +/// +/// Note: There is no \c TypeLoc for the deduced type and thus no +/// \c getDeducedLoc() matcher. +/// +/// Given +/// \code +/// auto a = 1; +/// auto b = 2.0; +/// \endcode +/// autoType(hasDeducedType(isInteger())) +/// matches "auto a" +/// +/// Usable as: Matcher +AST_TYPE_TRAVERSE_MATCHER(hasDeducedType, getDeducedType); + +/// \brief Matches block pointer types, i.e. types syntactically represented as +/// "void (^)(int)". +/// +/// The \c pointee is always required to be a \c FunctionType. +AST_TYPE_MATCHER(BlockPointerType, blockPointerType); + +/// \brief Matches member pointer types. +/// Given +/// \code +/// struct A { int i; } +/// A::* ptr = A::i; +/// \endcode +/// memberPointerType() +/// matches "A::* ptr" +AST_TYPE_MATCHER(MemberPointerType, memberPointerType); + +/// \brief Matches pointer types. +/// +/// Given +/// \code +/// int *a; +/// int &b = *a; +/// int c = 5; +/// \endcode +/// pointerType() +/// matches "int *a" +AST_TYPE_MATCHER(PointerType, pointerType); + +/// \brief Matches reference types. +/// +/// Given +/// \code +/// int *a; +/// int &b = *a; +/// int c = 5; +/// \endcode +/// pointerType() +/// matches "int &b" +AST_TYPE_MATCHER(ReferenceType, referenceType); + +/// \brief Narrows PointerType (and similar) matchers to those where the +/// \c pointee matches a given matcher. +/// +/// Given +/// \code +/// int *a; +/// int const *b; +/// float const *f; +/// \endcode +/// pointerType(pointee(isConstQualified(), isInteger())) +/// matches "int const *b" +/// +/// Usable as: Matcher, Matcher, +/// Matcher, Matcher +AST_TYPELOC_TRAVERSE_MATCHER(pointee, getPointee); + +/// \brief Matches typedef types. +/// +/// Given +/// \code +/// typedef int X; +/// \endcode +/// typedefType() +/// matches "typedef int X" +AST_TYPE_MATCHER(TypedefType, typedefType); + +/// \brief Matches \c TypedefTypes referring to a specific +/// \c TypedefNameDecl. +AST_MATCHER_P(TypedefType, hasDecl, + internal::Matcher, InnerMatcher) { + return InnerMatcher.matches(*Node.getDecl(), Finder, Builder); +} + /// \brief Matches nested name specifiers. /// /// Given @@ -2468,8 +2745,6 @@ inline internal::BindableMatcher loc( /// \brief Matches nested name specifiers that specify a type matching the /// given \c QualType matcher without qualifiers. -/// FIXME: This is a temporary solution. Switch to using Type-matchers as soon -/// as we have those. /// /// Given /// \code @@ -2485,8 +2760,23 @@ AST_MATCHER_P(NestedNameSpecifier, specifiesType, return InnerMatcher.matches(QualType(Node.getAsType(), 0), Finder, Builder); } -/// \brief Matches on the prefix of a \c NestedNameSpecifier or -/// \c NestedNameSpecifierLoc. +/// \brief Matches nested name specifier locs that specify a type matching the +/// given \c TypeLoc. +/// +/// Given +/// \code +/// struct A { struct B { struct C {}; }; }; +/// A::B::C c; +/// \endcode +/// nestedNameSpecifierLoc(specifiesTypeLoc(loc(type( +/// hasDeclaration(recordDecl(hasName("A"))))))) +/// matches "A::" +AST_MATCHER_P(NestedNameSpecifierLoc, specifiesTypeLoc, + internal::Matcher, InnerMatcher) { + return InnerMatcher.matches(Node.getTypeLoc(), Finder, Builder); +} + +/// \brief Matches on the prefix of a \c NestedNameSpecifier. /// /// Given /// \code @@ -2494,9 +2784,27 @@ AST_MATCHER_P(NestedNameSpecifier, specifiesType, /// A::B::C c; /// \endcode /// nestedNameSpecifier(hasPrefix(specifiesType(asString("struct A")))) and +/// matches "A::" +inline internal::Matcher hasPrefix( + const internal::Matcher &InnerMatcher) { + return internal::makeMatcher( + new internal::NestedNameSpecifierPrefixMatcher(InnerMatcher)); +} + +/// \brief Matches on the prefix of a \c NestedNameSpecifierLoc. +/// +/// Given +/// \code +/// struct A { struct B { struct C {}; }; }; +/// A::B::C c; +/// \endcode /// nestedNameSpecifierLoc(hasPrefix(loc(specifiesType(asString("struct A"))))) -/// both match "A::" -LOC_TRAVERSE_MATCHER(hasPrefix, NestedNameSpecifier, getPrefix) +/// matches "A::" +inline internal::Matcher hasPrefix( + const internal::Matcher &InnerMatcher) { + return internal::makeMatcher( + new internal::NestedNameSpecifierLocPrefixMatcher(InnerMatcher)); +} /// \brief Matches nested name specifiers that specify a namespace matching the /// given namespace matcher. diff --git a/include/clang/ASTMatchers/ASTMatchersInternal.h b/include/clang/ASTMatchers/ASTMatchersInternal.h index a79c30a2cf..da6ce18b42 100644 --- a/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -268,6 +268,16 @@ public: !llvm::is_same::value >::type* = 0) : Implementation(new ImplicitCastMatcher(Other)) {} + /// \brief Implicitly converts \c Matcher to \c Matcher. + /// + /// The resulting matcher is not strict, i.e. ignores qualifiers. + template + Matcher(const Matcher &Other, + typename llvm::enable_if_c< + llvm::is_same::value && + llvm::is_same::value >::type* = 0) + : Implementation(new TypeToQualType(Other)) {} + /// \brief Forwards the call to the underlying MatcherInterface pointer. bool matches(const T &Node, ASTMatchFinder *Finder, @@ -291,6 +301,29 @@ public: return matches(*Node, Finder, Builder); } + /// \brief Allows the conversion of a \c Matcher to a \c + /// Matcher. + /// + /// Depending on the constructor argument, the matcher is either strict, i.e. + /// does only matches in the absence of qualifiers, or not, i.e. simply + /// ignores any qualifiers. + template + class TypeToQualType : public MatcherInterface { + public: + TypeToQualType(const Matcher &InnerMatcher) + : InnerMatcher(InnerMatcher) {} + + virtual bool matches(const QualType &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + if (Node.isNull()) + return false; + return InnerMatcher.matches(*Node, Finder, Builder); + } + private: + const Matcher InnerMatcher; + }; + private: /// \brief Allows conversion from Matcher to Matcher if T /// is derived from Base. @@ -950,7 +983,6 @@ class IsExplicitTemplateSpecializationMatcher : public MatcherInterface { } }; - class IsArrowMatcher : public SingleNodeMatcherInterface { public: virtual bool matchesNode(const MemberExpr &Node) const { @@ -1011,7 +1043,7 @@ template class LocMatcher : public MatcherInterface { public: explicit LocMatcher(const Matcher &InnerMatcher) - : InnerMatcher(InnerMatcher) {} + : InnerMatcher(InnerMatcher) {} virtual bool matches(const TLoc &Node, ASTMatchFinder *Finder, @@ -1025,61 +1057,128 @@ private: const NestedNameSpecifier *extract(const NestedNameSpecifierLoc &Loc) const { return Loc.getNestedNameSpecifier(); } - // FIXME: Add overload for TypeLoc when implementing TypeLoc-matchers. const Matcher InnerMatcher; }; +/// \brief Matches \c NestedNameSpecifiers with a prefix matching another +/// \c Matcher. +class NestedNameSpecifierPrefixMatcher + : public MatcherInterface { +public: + explicit NestedNameSpecifierPrefixMatcher( + const Matcher &InnerMatcher) + : InnerMatcher(InnerMatcher) {} + + virtual bool matches(const NestedNameSpecifier &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + NestedNameSpecifier *NextNode = Node.getPrefix(); + if (NextNode == NULL) + return false; + return InnerMatcher.matches(*NextNode, Finder, Builder); + } + +private: + const Matcher InnerMatcher; +}; + +/// \brief Matches \c NestedNameSpecifierLocs with a prefix matching another +/// \c Matcher. +class NestedNameSpecifierLocPrefixMatcher + : public MatcherInterface { +public: + explicit NestedNameSpecifierLocPrefixMatcher( + const Matcher &InnerMatcher) + : InnerMatcher(InnerMatcher) {} + + virtual bool matches(const NestedNameSpecifierLoc &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + NestedNameSpecifierLoc NextNode = Node.getPrefix(); + if (!NextNode) + return false; + return InnerMatcher.matches(NextNode, Finder, Builder); + } + +private: + const Matcher InnerMatcher; +}; + +/// \brief Matches \c TypeLocs based on an inner matcher matching a certain +/// \c QualType. +/// +/// Used to implement the \c loc() matcher. +class TypeLocTypeMatcher : public MatcherInterface { +public: + explicit TypeLocTypeMatcher(const Matcher &InnerMatcher) + : InnerMatcher(InnerMatcher) {} + + virtual bool matches(const TypeLoc &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + if (!Node) + return false; + return InnerMatcher.matches(Node.getType(), Finder, Builder); + } + +private: + const Matcher InnerMatcher; +}; + /// \brief Matches nodes of type \c T for which the inner matcher matches on a /// another node of type \c T that can be reached using a given traverse /// function. template -class TraverseMatcher : public MatcherInterface { +class TypeTraverseMatcher : public MatcherInterface { public: - explicit TraverseMatcher(const Matcher &InnerMatcher, - T *(T::*TraverseFunction)() const) + explicit TypeTraverseMatcher(const Matcher &InnerMatcher, + QualType (T::*TraverseFunction)() const) : InnerMatcher(InnerMatcher), TraverseFunction(TraverseFunction) {} virtual bool matches(const T &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const { - T* NextNode = (Node.*TraverseFunction)(); - if (NextNode == NULL) + QualType NextNode = (Node.*TraverseFunction)(); + if (NextNode.isNull()) return false; - return InnerMatcher.matches(*NextNode, Finder, Builder); + return InnerMatcher.matches(NextNode, Finder, Builder); } private: - const Matcher InnerMatcher; - T *(T::*TraverseFunction)() const; + const Matcher InnerMatcher; + QualType (T::*TraverseFunction)() const; }; /// \brief Matches nodes of type \c T in a ..Loc hierarchy, for which the inner /// matcher matches on a another node of type \c T that can be reached using a /// given traverse function. template -class LocTraverseMatcher : public MatcherInterface { +class TypeLocTraverseMatcher : public MatcherInterface { public: - explicit LocTraverseMatcher(const Matcher &InnerMatcher, - T (T::*TraverseFunction)() const) + explicit TypeLocTraverseMatcher(const Matcher &InnerMatcher, + TypeLoc (T::*TraverseFunction)() const) : InnerMatcher(InnerMatcher), TraverseFunction(TraverseFunction) {} virtual bool matches(const T &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const { - if (!Node) - return false; - T NextNode = (Node.*TraverseFunction)(); + TypeLoc NextNode = (Node.*TraverseFunction)(); if (!NextNode) return false; return InnerMatcher.matches(NextNode, Finder, Builder); } private: - const Matcher InnerMatcher; - T (T::*TraverseFunction)() const; + const Matcher InnerMatcher; + TypeLoc (T::*TraverseFunction)() const; }; +template +T makeTypeAllOfComposite(ArrayRef *> InnerMatchers) { + return T(makeAllOfComposite(InnerMatchers)); +} + } // end namespace internal } // end namespace ast_matchers } // end namespace clang diff --git a/include/clang/ASTMatchers/ASTMatchersMacros.h b/include/clang/ASTMatchers/ASTMatchersMacros.h index 1bbbfaa3ae..953abc2a2e 100644 --- a/include/clang/ASTMatchers/ASTMatchersMacros.h +++ b/include/clang/ASTMatchers/ASTMatchersMacros.h @@ -221,23 +221,69 @@ const NodeType &Node, ASTMatchFinder *Finder, \ BoundNodesTreeBuilder *Builder) const -/// \brief LOC_TRAVERSE_MATCHER(MatcherName, NodeType, FunctionName) -/// defines the matcher \c MatcherName that can be used to traverse -/// a Type or NestedNameSpecifier as well as the corresponding ..Loc. +/// \brief Creates a variadic matcher for both a specific \c Type as well as +/// the corresponding \c TypeLoc. +#define AST_TYPE_MATCHER(NodeType, MatcherName) \ + const internal::VariadicDynCastAllOfMatcher MatcherName; \ + const internal::VariadicDynCastAllOfMatcher MatcherName##Loc + +/// \brief AST_TYPE_TRAVERSE_MATCHER(MatcherName, FunctionName) defines +/// the matcher \c MatcherName that can be used to traverse from one \c Type +/// to another. /// -/// The traversal is done using the given \c FunctionName. -#define LOC_TRAVERSE_MATCHER( \ - MatcherName, NodeType, FunctionName) \ - inline internal::Matcher hasPrefix( \ - const internal::Matcher &InnerMatcher) { \ - return internal::makeMatcher(new internal::TraverseMatcher( \ - InnerMatcher, &NodeType::getPrefix)); \ +/// For a specific \c SpecificType, the traversal is done using +/// \c SpecificType::FunctionName. The existance of such a function determines +/// whether a corresponding matcher can be used on \c SpecificType. +#define AST_TYPE_TRAVERSE_MATCHER(MatcherName, FunctionName) \ +class Polymorphic##MatcherName##TypeMatcher { \ +public: \ + Polymorphic##MatcherName##TypeMatcher( \ + const internal::Matcher &InnerMatcher) \ + : InnerMatcher(InnerMatcher) {} \ + template operator internal::Matcher() { \ + return internal::Matcher(new internal::TypeTraverseMatcher( \ + InnerMatcher, &T::FunctionName)); \ } \ - inline internal::Matcher hasPrefix( \ - const internal::Matcher &InnerMatcher) { \ - return internal::makeMatcher( \ - new internal::LocTraverseMatcher( \ - InnerMatcher, &NodeType##Loc::getPrefix)); \ - } +private: \ + const internal::Matcher InnerMatcher; \ +}; \ +class Variadic##MatcherName##TypeTraverseMatcher \ + : public llvm::VariadicFunction< \ + Polymorphic##MatcherName##TypeMatcher, \ + internal::Matcher, \ + internal::makeTypeAllOfComposite< \ + Polymorphic##MatcherName##TypeMatcher, QualType> > { \ +public: \ + Variadic##MatcherName##TypeTraverseMatcher() {} \ +}; \ +const Variadic##MatcherName##TypeTraverseMatcher MatcherName + +/// \brief AST_TYPELOC_TRAVERSE_MATCHER(MatcherName, FunctionName) works +/// identical to \c AST_TYPE_TRAVERSE_MATCHER but operates on \c TypeLocs. +#define AST_TYPELOC_TRAVERSE_MATCHER(MatcherName, FunctionName) \ +class Polymorphic##MatcherName##TypeLocMatcher { \ +public: \ + Polymorphic##MatcherName##TypeLocMatcher( \ + const internal::Matcher &InnerMatcher) \ + : InnerMatcher(InnerMatcher) {} \ + template operator internal::Matcher() { \ + return internal::Matcher(new internal::TypeLocTraverseMatcher( \ + InnerMatcher, &T::FunctionName##Loc)); \ + } \ +private: \ + const internal::Matcher InnerMatcher; \ +}; \ +class Variadic##MatcherName##TypeLocTraverseMatcher \ + : public llvm::VariadicFunction< \ + Polymorphic##MatcherName##TypeLocMatcher, \ + internal::Matcher, \ + internal::makeTypeAllOfComposite< \ + Polymorphic##MatcherName##TypeLocMatcher, TypeLoc> > { \ +public: \ + Variadic##MatcherName##TypeLocTraverseMatcher() {} \ +}; \ +const Variadic##MatcherName##TypeLocTraverseMatcher MatcherName##Loc; \ +AST_TYPE_TRAVERSE_MATCHER(MatcherName, FunctionName##Type) #endif // LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_MACROS_H diff --git a/include/clang/ASTMatchers/ASTTypeTraits.h b/include/clang/ASTMatchers/ASTTypeTraits.h index 72ca2ad9da..ab67b17178 100644 --- a/include/clang/ASTMatchers/ASTTypeTraits.h +++ b/include/clang/ASTMatchers/ASTTypeTraits.h @@ -74,9 +74,11 @@ private: enum NodeTypeTag { NT_Decl, NT_Stmt, + NT_Type, NT_NestedNameSpecifier, NT_NestedNameSpecifierLoc, - NT_QualType + NT_QualType, + NT_TypeLoc } Tag; /// \brief Stores the data of the node. @@ -85,8 +87,11 @@ private: /// guaranteed to be unique pointers pointing to dedicated storage in the /// AST. \c QualTypes on the other hand do not have storage or unique /// pointers and thus need to be stored by value. - llvm::AlignedCharArrayUnion Storage; + llvm::AlignedCharArrayUnion + Storage; }; + +// FIXME: Pull out abstraction for the following. template struct DynTypedNode::BaseConverter >::type> { static const T *get(NodeTypeTag Tag, const char Storage[]) { @@ -115,6 +120,20 @@ template struct DynTypedNode::BaseConverter struct DynTypedNode::BaseConverter >::type> { + static const T *get(NodeTypeTag Tag, const char Storage[]) { + if (Tag == NT_Type) + return dyn_cast(*reinterpret_cast(Storage)); + return NULL; + } + static DynTypedNode create(const Type &Node) { + DynTypedNode Result; + Result.Tag = NT_Type; + new (Result.Storage.buffer) const Type*(&Node); + return Result; + } +}; template<> struct DynTypedNode::BaseConverter { static const NestedNameSpecifier *get(NodeTypeTag Tag, const char Storage[]) { if (Tag == NT_NestedNameSpecifier) @@ -155,6 +174,19 @@ template<> struct DynTypedNode::BaseConverter { return Result; } }; +template<> struct DynTypedNode::BaseConverter { + static const TypeLoc *get(NodeTypeTag Tag, const char Storage[]) { + if (Tag == NT_TypeLoc) + return reinterpret_cast(Storage); + return NULL; + } + static DynTypedNode create(const TypeLoc &Node) { + DynTypedNode Result; + Result.Tag = NT_TypeLoc; + new (Result.Storage.buffer) TypeLoc(Node); + return Result; + } +}; // The only operation we allow on unsupported types is \c get. // This allows to conveniently use \c DynTypedNode when having an arbitrary // AST node that is not supported, but prevents misuse - a user cannot create diff --git a/lib/ASTMatchers/ASTMatchFinder.cpp b/lib/ASTMatchers/ASTMatchFinder.cpp index 80ea16aa52..ebbadc424d 100644 --- a/lib/ASTMatchers/ASTMatchFinder.cpp +++ b/lib/ASTMatchers/ASTMatchFinder.cpp @@ -553,10 +553,15 @@ bool MatchASTVisitor::TraverseType(QualType TypeNode) { return RecursiveASTVisitor::TraverseType(TypeNode); } -bool MatchASTVisitor::TraverseTypeLoc(TypeLoc TypeLoc) { - match(TypeLoc.getType()); - return RecursiveASTVisitor:: - TraverseTypeLoc(TypeLoc); +bool MatchASTVisitor::TraverseTypeLoc(TypeLoc TypeLocNode) { + // The RecursiveASTVisitor only visits types if they're not within TypeLocs. + // We still want to find those types via matchers, so we match them here. Note + // that the TypeLocs are structurally a shadow-hierarchy to the expressed + // type, so we visit all involved parts of a compound type when matching on + // each TypeLoc. + match(TypeLocNode); + match(TypeLocNode.getType()); + return RecursiveASTVisitor::TraverseTypeLoc(TypeLocNode); } bool MatchASTVisitor::TraverseNestedNameSpecifier(NestedNameSpecifier *NNS) { @@ -649,6 +654,12 @@ void MatchFinder::addMatcher(const NestedNameSpecifierLocMatcher &NodeMatch, new NestedNameSpecifierLocMatcher(NodeMatch), Action)); } +void MatchFinder::addMatcher(const TypeLocMatcher &NodeMatch, + MatchCallback *Action) { + MatcherCallbackPairs.push_back(std::make_pair( + new TypeLocMatcher(NodeMatch), Action)); +} + ASTConsumer *MatchFinder::newASTConsumer() { return new internal::MatchASTConsumer(&MatcherCallbackPairs, ParsingDone); } diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp index d8f5511d6d..3dc4e1defb 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -2884,6 +2884,185 @@ TEST(HasAncestor, MatchesInImplicitCode) { hasAncestor(recordDecl(hasName("A"))))))))); } +TEST(TypeMatching, MatchesTypes) { + EXPECT_TRUE(matches("struct S {};", qualType().bind("loc"))); +} + +TEST(TypeMatching, MatchesArrayTypes) { + EXPECT_TRUE(matches("int a[] = {2,3};", arrayType())); + EXPECT_TRUE(matches("int a[42];", arrayType())); + EXPECT_TRUE(matches("void f(int b) { int a[b]; }", arrayType())); + + EXPECT_TRUE(notMatches("struct A {}; A a[7];", + arrayType(hasElementType(builtinType())))); + + EXPECT_TRUE(matches( + "int const a[] = { 2, 3 };", + qualType(arrayType(hasElementType(builtinType()))))); + EXPECT_TRUE(matches( + "int const a[] = { 2, 3 };", + qualType(isConstQualified(), arrayType(hasElementType(builtinType()))))); + EXPECT_TRUE(matches( + "typedef const int T; T x[] = { 1, 2 };", + qualType(isConstQualified(), arrayType()))); + + EXPECT_TRUE(notMatches( + "int a[] = { 2, 3 };", + qualType(isConstQualified(), arrayType(hasElementType(builtinType()))))); + EXPECT_TRUE(notMatches( + "int a[] = { 2, 3 };", + qualType(arrayType(hasElementType(isConstQualified(), builtinType()))))); + EXPECT_TRUE(notMatches( + "int const a[] = { 2, 3 };", + qualType(arrayType(hasElementType(builtinType())), + unless(isConstQualified())))); + + EXPECT_TRUE(matches("int a[2];", + constantArrayType(hasElementType(builtinType())))); + EXPECT_TRUE(matches("const int a = 0;", qualType(isInteger()))); +} + +TEST(TypeMatching, MatchesComplexTypes) { + EXPECT_TRUE(matches("_Complex float f;", complexType())); + EXPECT_TRUE(matches( + "_Complex float f;", + complexType(hasElementType(builtinType())))); + EXPECT_TRUE(notMatches( + "_Complex float f;", + complexType(hasElementType(isInteger())))); +} + +TEST(TypeMatching, MatchesConstantArrayTypes) { + EXPECT_TRUE(matches("int a[2];", constantArrayType())); + EXPECT_TRUE(notMatches( + "void f() { int a[] = { 2, 3 }; int b[a[0]]; }", + constantArrayType(hasElementType(builtinType())))); + + EXPECT_TRUE(matches("int a[42];", constantArrayType(hasSize(42)))); + EXPECT_TRUE(matches("int b[2*21];", constantArrayType(hasSize(42)))); + EXPECT_TRUE(notMatches("int c[41], d[43];", constantArrayType(hasSize(42)))); +} + +TEST(TypeMatching, MatchesDependentSizedArrayTypes) { + EXPECT_TRUE(matches( + "template class array { T data[Size]; };", + dependentSizedArrayType())); + EXPECT_TRUE(notMatches( + "int a[42]; int b[] = { 2, 3 }; void f() { int c[b[0]]; }", + dependentSizedArrayType())); +} + +TEST(TypeMatching, MatchesIncompleteArrayType) { + EXPECT_TRUE(matches("int a[] = { 2, 3 };", incompleteArrayType())); + EXPECT_TRUE(matches("void f(int a[]) {}", incompleteArrayType())); + + EXPECT_TRUE(notMatches("int a[42]; void f() { int b[a[0]]; }", + incompleteArrayType())); +} + +TEST(TypeMatching, MatchesVariableArrayType) { + EXPECT_TRUE(matches("void f(int b) { int a[b]; }", variableArrayType())); + EXPECT_TRUE(notMatches("int a[] = {2, 3}; int b[42];", variableArrayType())); + + EXPECT_TRUE(matches( + "void f(int b) { int a[b]; }", + variableArrayType(hasSizeExpr(ignoringImpCasts(declRefExpr(to( + varDecl(hasName("b"))))))))); +} + +TEST(TypeMatching, MatchesAtomicTypes) { + EXPECT_TRUE(matches("_Atomic(int) i;", atomicType())); + + EXPECT_TRUE(matches("_Atomic(int) i;", + atomicType(hasValueType(isInteger())))); + EXPECT_TRUE(notMatches("_Atomic(float) f;", + atomicType(hasValueType(isInteger())))); +} + +TEST(TypeMatching, MatchesAutoTypes) { + EXPECT_TRUE(matches("auto i = 2;", autoType())); + EXPECT_TRUE(matches("int v[] = { 2, 3 }; void f() { for (int i : v) {} }", + autoType())); + + EXPECT_TRUE(matches("auto a = 1;", + autoType(hasDeducedType(isInteger())))); + EXPECT_TRUE(notMatches("auto b = 2.0;", + autoType(hasDeducedType(isInteger())))); +} + +TEST(TypeMatching, PointerTypes) { + EXPECT_TRUE(matchAndVerifyResultTrue( + "int* a;", + pointerTypeLoc(pointeeLoc(typeLoc().bind("loc"))), + new VerifyIdIsBoundTo("loc", 1))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "int* a;", + pointerTypeLoc().bind("loc"), + new VerifyIdIsBoundTo("loc", 1))); + EXPECT_TRUE(matches( + "int** a;", + pointerTypeLoc(pointeeLoc(loc(qualType()))))); + EXPECT_TRUE(matches( + "int** a;", + loc(pointerType(pointee(pointerType()))))); + EXPECT_TRUE(matches( + "int* b; int* * const a = &b;", + loc(qualType(isConstQualified(), pointerType())))); + + std::string Fragment = "struct A { int i; }; int A::* ptr = &A::i;"; + EXPECT_TRUE(notMatches(Fragment, blockPointerType())); + EXPECT_TRUE(matches(Fragment, memberPointerType())); + EXPECT_TRUE(notMatches(Fragment, pointerType())); + EXPECT_TRUE(notMatches(Fragment, referenceType())); + + Fragment = "int *I;"; + EXPECT_TRUE(notMatches(Fragment, blockPointerType())); + EXPECT_TRUE(notMatches(Fragment, memberPointerType())); + EXPECT_TRUE(matches(Fragment, pointerType())); + EXPECT_TRUE(notMatches(Fragment, referenceType())); + + Fragment = "int a; int &b = a;"; + EXPECT_TRUE(notMatches(Fragment, blockPointerType())); + EXPECT_TRUE(notMatches(Fragment, memberPointerType())); + EXPECT_TRUE(notMatches(Fragment, pointerType())); + EXPECT_TRUE(matches(Fragment, referenceType())); +} + +TEST(TypeMatching, PointeeTypes) { + EXPECT_TRUE(matches("int b; int &a = b;", + referenceType(pointee(builtinType())))); + EXPECT_TRUE(matches("int *a;", pointerType(pointee(builtinType())))); + + EXPECT_TRUE(matches("int *a;", + pointerTypeLoc(pointeeLoc(loc(builtinType()))))); + + EXPECT_TRUE(matches( + "int const *A;", + pointerType(pointee(isConstQualified(), builtinType())))); + EXPECT_TRUE(notMatches( + "int *A;", + pointerType(pointee(isConstQualified(), builtinType())))); +} + +TEST(TypeMatching, MatchesPointersToConstTypes) { + EXPECT_TRUE(matches("int b; int * const a = &b;", + loc(pointerType()))); + EXPECT_TRUE(matches("int b; int * const a = &b;", + pointerTypeLoc())); + EXPECT_TRUE(matches( + "int b; const int * a = &b;", + pointerTypeLoc(pointeeLoc(builtinTypeLoc())))); + EXPECT_TRUE(matches( + "int b; const int * a = &b;", + pointerType(pointee(builtinType())))); +} + +TEST(TypeMatching, MatchesTypedefTypes) { + EXPECT_TRUE(matches("typedef int X;", typedefType())); + + EXPECT_TRUE(matches("typedef int X;", typedefType(hasDecl(decl())))); +} + TEST(NNS, MatchesNestedNameSpecifiers) { EXPECT_TRUE(matches("namespace ns { struct A {}; } ns::A a;", nestedNameSpecifier())); @@ -2942,8 +3121,8 @@ TEST(NNS, MatchesNestedNameSpecifierPrefixes) { nestedNameSpecifier(hasPrefix(specifiesType(asString("struct A")))))); EXPECT_TRUE(matches( "struct A { struct B { struct C {}; }; }; A::B::C c;", - nestedNameSpecifierLoc(hasPrefix(loc( - specifiesType(asString("struct A"))))))); + nestedNameSpecifierLoc(hasPrefix( + specifiesTypeLoc(loc(qualType(asString("struct A")))))))); } } // end namespace ast_matchers