Added more tests where the behavior at the moment is counter-intuitive with
the patch's current version.
Hi klimek, chandlerc, rsmith, mdiamond,
http://llvm-reviews.chandlerc.com/D47
CHANGE SINCE LAST DIFF
http://llvm-reviews.chandlerc.com/D47?vs=120&id=121#toc
Files:
include/clang/ASTMatchers/ASTMatchFinder.h
include/clang/ASTMatchers/ASTMatchers.h
include/clang/ASTMatchers/ASTMatchersInternal.h
include/clang/ASTMatchers/ASTMatchersMacros.h
include/clang/ASTMatchers/ASTTypeTraits.h
lib/ASTMatchers/ASTMatchFinder.cpp
unittests/ASTMatchers/ASTMatchersTest.cpp
Index: include/clang/ASTMatchers/ASTMatchFinder.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchFinder.h
+++ include/clang/ASTMatchers/ASTMatchFinder.h
@@ -116,6 +116,8 @@
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.
Index: include/clang/ASTMatchers/ASTMatchers.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -108,8 +108,9 @@
/// hierarchy.
/// @{
typedef internal::Matcher<Decl> DeclarationMatcher;
-typedef internal::Matcher<QualType> TypeMatcher;
typedef internal::Matcher<Stmt> StatementMatcher;
+typedef internal::Matcher<QualType> TypeMatcher;
+typedef internal::Matcher<TypeLoc> TypeLocMatcher;
typedef internal::Matcher<NestedNameSpecifier> NestedNameSpecifierMatcher;
typedef internal::Matcher<NestedNameSpecifierLoc> NestedNameSpecifierLocMatcher;
/// @}
@@ -2408,6 +2409,42 @@
internal::IsExplicitTemplateSpecializationMatcher>();
}
+/// \brief Matches \c QualTypes in the clang AST.
+const internal::VariadicAllOfMatcher<QualType> qualType;
+const internal::VariadicDynCastAllOfMatcher<Type, Type> type;
+const internal::VariadicDynCastAllOfMatcher<TypeLoc, TypeLoc> typeLoc;
+
+/// \brief Converts Matcher<Type> to Matcher<QualType> ignoring any qualifiers.
+inline internal::Matcher<QualType> anyQualifiers(
+ const internal::Matcher<Type> &InnerMatcher) {
+ return internal::Matcher<QualType>(
+ new internal::Matcher<Type>::TypeToQualType<Type>(InnerMatcher, false));
+}
+
+/// \brief Matches \c TypeLocs for which the given inner
+/// QualType-matcher matches.
+inline internal::BindableMatcher<TypeLoc> loc(
+ const internal::Matcher<QualType> &InnerMatcher) {
+ return internal::BindableMatcher<TypeLoc>(
+ new internal::TypeLocTypeMatcher(InnerMatcher));
+}
+
+/// \brief Matchers for \c Types and \c TypeLocs as well as matchers for
+/// each of the hierarchies.
+///
+/// For details, see the documentation for the corresonding AST nodes.
+/// @{
+AST_TYPE_MATCHER(ArrayType, arrayType);
+AST_TYPE_TRAVERSE_MATCHER(ArrayType, hasElementType, getElement);
+
+AST_TYPE_MATCHER(BuiltinType, builtinType);
+
+AST_TYPE_MATCHER(PointerType, pointerType);
+AST_TYPE_TRAVERSE_MATCHER(PointerType, pointee, getPointee);
+
+AST_TYPE_MATCHER(TypedefType, typedefType);
+/// @}
+
/// \brief Matches nested name specifiers.
///
/// Given
@@ -2454,6 +2491,10 @@
return false;
return InnerMatcher.matches(QualType(Node.getAsType(), 0), Finder, Builder);
}
+AST_MATCHER_P(NestedNameSpecifierLoc, specifiesTypeLoc,
+ internal::Matcher<TypeLoc>, InnerMatcher) {
+ return InnerMatcher.matches(Node.getTypeLoc(), Finder, Builder);
+}
/// \brief Matches on the prefix of a \c NestedNameSpecifier or
/// \c NestedNameSpecifierLoc.
@@ -2466,7 +2507,14 @@
/// nestedNameSpecifier(hasPrefix(specifiesType(asString("struct A")))) and
/// nestedNameSpecifierLoc(hasPrefix(loc(specifiesType(asString("struct A")))))
/// both match "A::"
-LOC_TRAVERSE_MATCHER(hasPrefix, NestedNameSpecifier, getPrefix)
+inline internal::Matcher<NestedNameSpecifier> hasPrefix(
+ const internal::Matcher<NestedNameSpecifier> &InnerMatcher) {
+ return internal::makeMatcher(new internal::NNSPrefixMatcher(InnerMatcher));
+}
+inline internal::Matcher<NestedNameSpecifierLoc> hasPrefix(
+ const internal::Matcher<NestedNameSpecifierLoc> &InnerMatcher) {
+ return internal::makeMatcher(new internal::NNSLocPrefixMatcher(InnerMatcher));
+}
/// \brief Matches nested name specifiers that specify a namespace matching the
/// given namespace matcher.
Index: include/clang/ASTMatchers/ASTMatchersInternal.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchersInternal.h
+++ include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -268,6 +268,17 @@
!llvm::is_same<From, T>::value >::type* = 0)
: Implementation(new ImplicitCastMatcher<From>(Other)) {}
+ /// \brief Implicitly converts \c Matcher<Type> to \c Matcher<QualType>.
+ ///
+ /// The resulting matcher is strict, i.e. only matches on types without
+ /// qualifiers.
+ template <typename TypeT>
+ Matcher(const Matcher<TypeT> &Other,
+ typename llvm::enable_if_c<
+ llvm::is_same<T, QualType>::value &&
+ llvm::is_same<TypeT, Type>::value >::type* = 0)
+ : Implementation(new TypeToQualType<TypeT>(Other, /*Strict=*/true)) {}
+
/// \brief Forwards the call to the underlying MatcherInterface<T> pointer.
bool matches(const T &Node,
ASTMatchFinder *Finder,
@@ -291,6 +302,32 @@
return matches(*Node, Finder, Builder);
}
+ /// \brief Allows the conversion of a \c Matcher<Type> to a \c
+ /// Matcher<QualType>.
+ ///
+ /// 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 <typename TypeT>
+ class TypeToQualType : public MatcherInterface<QualType> {
+ public:
+ TypeToQualType(const Matcher<TypeT> &InnerMatcher, bool Strict)
+ : InnerMatcher(InnerMatcher), Strict(Strict) {}
+
+ virtual bool matches(const QualType &Node,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ if (Node.isNull())
+ return false;
+ if (Strict && Node.hasQualifiers())
+ return false;
+ return InnerMatcher.matches(*Node, Finder, Builder);
+ }
+ private:
+ const Matcher<TypeT> InnerMatcher;
+ bool Strict;
+ };
+
private:
/// \brief Allows conversion from Matcher<Base> to Matcher<T> if T
/// is derived from Base.
@@ -1025,59 +1062,110 @@
const NestedNameSpecifier *extract(const NestedNameSpecifierLoc &Loc) const {
return Loc.getNestedNameSpecifier();
}
- // FIXME: Add overload for TypeLoc when implementing TypeLoc-matchers.
const Matcher<T> InnerMatcher;
};
+class TypeLocTypeMatcher : public MatcherInterface<TypeLoc> {
+public:
+ explicit TypeLocTypeMatcher(const Matcher<QualType> &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<QualType> InnerMatcher;
+};
+
+class NNSPrefixMatcher : public MatcherInterface<NestedNameSpecifier> {
+public:
+ explicit NNSPrefixMatcher(const Matcher<NestedNameSpecifier> &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<NestedNameSpecifier> InnerMatcher;
+};
+
+class NNSLocPrefixMatcher : public MatcherInterface<NestedNameSpecifierLoc> {
+public:
+ explicit NNSLocPrefixMatcher(
+ const Matcher<NestedNameSpecifierLoc> &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<NestedNameSpecifierLoc> 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 <typename T>
-class TraverseMatcher : public MatcherInterface<T> {
+class TypeTraverseMatcher : public MatcherInterface<T> {
public:
- explicit TraverseMatcher(const Matcher<T> &InnerMatcher,
- T *(T::*TraverseFunction)() const)
+ explicit TypeTraverseMatcher(const Matcher<QualType> &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<T> InnerMatcher;
- T *(T::*TraverseFunction)() const;
+ const Matcher<QualType> 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 <typename T>
-class LocTraverseMatcher : public MatcherInterface<T> {
+class TypeLocTraverseMatcher : public MatcherInterface<T> {
public:
- explicit LocTraverseMatcher(const Matcher<T> &InnerMatcher,
- T (T::*TraverseFunction)() const)
+ explicit TypeLocTraverseMatcher(const Matcher<TypeLoc> &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<T> InnerMatcher;
- T (T::*TraverseFunction)() const;
+ const Matcher<TypeLoc> InnerMatcher;
+ TypeLoc (T::*TraverseFunction)() const;
};
} // end namespace internal
Index: include/clang/ASTMatchers/ASTMatchersMacros.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchersMacros.h
+++ include/clang/ASTMatchers/ASTMatchersMacros.h
@@ -221,23 +221,31 @@
const NodeType &Node, ASTMatchFinder *Finder, \
BoundNodesTreeBuilder *Builder) const
-/// \brief LOC_TRAVERSE_MATCHER(MatcherName, NodeType, FunctionName)
+#define AST_TYPE_MATCHER(NodeType, MatcherName) \
+ const internal::VariadicDynCastAllOfMatcher<Type, NodeType> MatcherName; \
+ const internal::VariadicDynCastAllOfMatcher<TypeLoc, \
+ NodeType##Loc> MatcherName##Loc
+
+/// \brief TYPE_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.
///
/// The traversal is done using the given \c FunctionName.
-#define LOC_TRAVERSE_MATCHER( \
- MatcherName, NodeType, FunctionName) \
- inline internal::Matcher<NodeType> hasPrefix( \
- const internal::Matcher<NodeType> &InnerMatcher) { \
- return internal::makeMatcher(new internal::TraverseMatcher<NodeType>( \
- InnerMatcher, &NodeType::getPrefix)); \
+#define AST_TYPE_TRAVERSE_MATCHER( \
+ NodeType, MatcherName, FunctionName) \
+ inline internal::Matcher<NodeType> MatcherName( \
+ const internal::Matcher<QualType> &InnerMatcher) { \
+ return internal::makeMatcher( \
+ new internal::TypeTraverseMatcher<NodeType>( \
+ InnerMatcher, &NodeType::FunctionName##Type)); \
} \
- inline internal::Matcher<NodeType##Loc> hasPrefix( \
- const internal::Matcher<NodeType##Loc> &InnerMatcher) { \
+ inline internal::Matcher<NodeType##Loc> MatcherName##Loc( \
+ const internal::Matcher<TypeLoc> &InnerMatcher) { \
return internal::makeMatcher( \
- new internal::LocTraverseMatcher<NodeType##Loc>( \
- InnerMatcher, &NodeType##Loc::getPrefix)); \
- }
+ new internal::TypeLocTraverseMatcher<NodeType##Loc>( \
+ InnerMatcher, &NodeType##Loc::FunctionName##Loc)); \
+ } \
+ inline internal::Matcher<NodeType> MatcherName( \
+ const internal::Matcher<QualType> &InnerMatcher)
#endif // LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_MACROS_H
Index: include/clang/ASTMatchers/ASTTypeTraits.h
===================================================================
--- include/clang/ASTMatchers/ASTTypeTraits.h
+++ include/clang/ASTMatchers/ASTTypeTraits.h
@@ -74,18 +74,21 @@
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.
///
/// Note that we can store \c Decls and \c Stmts by pointer as they are
/// 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<Decl*, Stmt*, NestedNameSpecifierLoc, QualType> Storage;
+ llvm::AlignedCharArrayUnion<Decl*, QualType, TypeLoc, NestedNameSpecifierLoc>
+ Storage;
};
template<typename T> struct DynTypedNode::BaseConverter<T,
typename llvm::enable_if<llvm::is_base_of<Decl, T> >::type> {
@@ -115,6 +118,20 @@
return Result;
}
};
+template<typename T> struct DynTypedNode::BaseConverter<T,
+ typename llvm::enable_if<llvm::is_base_of<Type, T> >::type> {
+ static const T *get(NodeTypeTag Tag, const char Storage[]) {
+ if (Tag == NT_Type)
+ return dyn_cast<T>(*reinterpret_cast<Type*const*>(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<NestedNameSpecifier, void> {
static const NestedNameSpecifier *get(NodeTypeTag Tag, const char Storage[]) {
if (Tag == NT_NestedNameSpecifier)
@@ -155,6 +172,19 @@
return Result;
}
};
+template<> struct DynTypedNode::BaseConverter<TypeLoc, void> {
+ static const TypeLoc *get(NodeTypeTag Tag, const char Storage[]) {
+ if (Tag == NT_TypeLoc)
+ return reinterpret_cast<const TypeLoc*>(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
Index: lib/ASTMatchers/ASTMatchFinder.cpp
===================================================================
--- lib/ASTMatchers/ASTMatchFinder.cpp
+++ lib/ASTMatchers/ASTMatchFinder.cpp
@@ -553,10 +553,10 @@
return RecursiveASTVisitor<MatchASTVisitor>::TraverseType(TypeNode);
}
-bool MatchASTVisitor::TraverseTypeLoc(TypeLoc TypeLoc) {
- match(TypeLoc.getType());
- return RecursiveASTVisitor<MatchASTVisitor>::
- TraverseTypeLoc(TypeLoc);
+bool MatchASTVisitor::TraverseTypeLoc(TypeLoc TypeLocNode) {
+ match(TypeLocNode);
+ match(TypeLocNode.getType());
+ return RecursiveASTVisitor<MatchASTVisitor>::TraverseTypeLoc(TypeLocNode);
}
bool MatchASTVisitor::TraverseNestedNameSpecifier(NestedNameSpecifier *NNS) {
@@ -649,6 +649,12 @@
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);
}
Index: unittests/ASTMatchers/ASTMatchersTest.cpp
===================================================================
--- unittests/ASTMatchers/ASTMatchersTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTest.cpp
@@ -2847,6 +2847,59 @@
hasAncestor(recordDecl(hasName("A")))))))));
}
+TEST(TypeMatching, MatchesTypes) {
+ EXPECT_TRUE(matches("struct S {};", qualType().bind("loc")));
+}
+
+TEST(TypeMatching, PointerTypes) {
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "int* a;",
+ pointerTypeLoc(pointeeLoc(typeLoc().bind("loc"))),
+ new VerifyIdIsBoundTo<TypeLoc>("loc", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "int* a;",
+ pointerTypeLoc().bind("loc"),
+ new VerifyIdIsBoundTo<TypeLoc>("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(), anyQualifiers(pointerType())))));
+
+ // A bit weird ..
+ EXPECT_TRUE(notMatches(
+ "int b; int * const a = &b;",
+ loc(pointerType())));
+ EXPECT_TRUE(matches(
+ "int b; int * const a = &b;",
+ pointerTypeLoc()));
+
+ // Not good .. Could we prevent users to write this?
+ EXPECT_TRUE(notMatches(
+ "int const a[] = { 2, 3 };",
+ qualType(isConstQualified(), arrayType(hasElementType(builtinType())))));
+
+ // Const hidden in typedefs .. I think this would be nicer without the
+ // anyQualifiers().
+ EXPECT_TRUE(matches(
+ "typedef const int T; T x[] = { 1, 2 };",
+ qualType(isConstQualified(), anyQualifiers(arrayType()))));
+
+ // Type- and TypeLoc-behavior different. I guess we could somehow use the
+ // QualType stored inside TypeLocs and simulate what is happening with types,
+ // but that would diverge more from the AST.
+ EXPECT_TRUE(matches(
+ "int b; const int * a = &b;",
+ pointerTypeLoc(pointeeLoc(builtinTypeLoc()))));
+ EXPECT_TRUE(notMatches(
+ "int b; const int * a = &b;",
+ pointerType(pointee(builtinType()))));
+}
+
TEST(NNS, MatchesNestedNameSpecifiers) {
EXPECT_TRUE(matches("namespace ns { struct A {}; } ns::A a;",
nestedNameSpecifier()));
@@ -2905,8 +2958,8 @@
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
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits