Hi klimek, chandlerc, rsmith, mdiamond,
Not polished for submitting yet, but a basis for discussions.
Main question: The patch allows an implicit conversion from Matcher<Type> to
Matcher<QualType>, which is probably a good idea.
It is currently implemented to be strict, i.e. the generated Matcher<QualType>
only matches QualTypes without qualifiers. After initial discussions it seemed
like this would be closer to the AST model. However, it leads to some bad and
unintuitive things (see tests for a few examples). Maybe we should reconsider?
http://llvm-reviews.chandlerc.com/D47
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,40 @@
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);
+/// @}
+
/// \brief Matches nested name specifiers.
///
/// Given
@@ -2454,6 +2489,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 +2505,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
@@ -651,6 +651,14 @@
Nodes->getNodeAs<NestedNameSpecifier>(Id)) {
llvm::raw_string_ostream OS(Name);
NNS->print(OS, PrintingPolicy(LangOptions()));
+ } else if (const TypeLoc *Loc =
+ Nodes->getNodeAs<TypeLoc>(Id)) {
+ llvm::outs() << Loc->getTypePtr()->getTypeClassName() << " "
+ << Loc->getLocStart().isValid() << "\n";
+ Loc->getTypePtr()->dump();
+ } else if (const Type *Ty = Nodes->getNodeAs<Type>(Id)) {
+ llvm::outs() << "Bound type is const: ";
+ Ty->dump();
}
return true;
}
@@ -2847,6 +2855,43 @@
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 ..
+ EXPECT_TRUE(notMatches(
+ "int const a[] = { 2, 3 };",
+ qualType(isConstQualified(), arrayType(hasElementType(builtinType())))));
+}
+
TEST(NNS, MatchesNestedNameSpecifiers) {
EXPECT_TRUE(matches("namespace ns { struct A {}; } ns::A a;",
nestedNameSpecifier()));
@@ -2905,8 +2950,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