Hi klimek,
http://llvm-reviews.chandlerc.com/D815
Files:
include/clang/AST/ASTTypeTraits.h
include/clang/ASTMatchers/ASTMatchersInternal.h
include/clang/ASTMatchers/Dynamic/VariantValue.h
lib/AST/ASTTypeTraits.cpp
lib/AST/CMakeLists.txt
lib/ASTMatchers/ASTMatchFinder.cpp
lib/ASTMatchers/Dynamic/Diagnostics.cpp
lib/ASTMatchers/Dynamic/Marshallers.h
lib/ASTMatchers/Dynamic/VariantValue.cpp
unittests/AST/ASTTypeTraitsTest.cpp
unittests/AST/CMakeLists.txt
unittests/ASTMatchers/ASTMatchersTest.cpp
unittests/ASTMatchers/Dynamic/ParserTest.cpp
unittests/ASTMatchers/Dynamic/RegistryTest.cpp
unittests/ASTMatchers/Dynamic/VariantValueTest.cpp
Index: include/clang/AST/ASTTypeTraits.h
===================================================================
--- include/clang/AST/ASTTypeTraits.h
+++ include/clang/AST/ASTTypeTraits.h
@@ -7,8 +7,9 @@
//
//===----------------------------------------------------------------------===//
//
-// Provides a dynamically typed node container that can be used to store
-// an AST base node at runtime in the same storage in a type safe way.
+// Provides a dynamic type identifier and a dynamically typed node container
+// that can be used to store an AST base node at runtime in the same storage in
+// a type safe way.
//
//===----------------------------------------------------------------------===//
@@ -18,11 +19,126 @@
#include "clang/AST/Decl.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/TypeLoc.h"
+#include "clang/Basic/LLVM.h"
#include "llvm/Support/AlignOf.h"
namespace clang {
+
+/// \brief Forward declare the types to be used below.
+#define DECL(DERIVED, BASE) class DERIVED##Decl;
+#include "clang/AST/DeclNodes.inc"
+#define STMT(DERIVED, BASE) class DERIVED;
+#include "clang/AST/StmtNodes.inc"
+#define TYPE(DERIVED, BASE) class DERIVED##Type;
+#include "clang/AST/TypeNodes.def"
+
namespace ast_type_traits {
+/// \brief Dynamic type identifier.
+///
+/// It can be constructed from any node type and allows for runtime type
+/// hierarchy checks.
+/// Use getFromNodeType<T>() to construct them. There are also shortcut
+/// functions defined for each base type.
+class DynNodeType {
+public:
+ /// \brief Empty identifier. It matches nothing.
+ DynNodeType() : TypeId(NTI_None) {}
+
+ /// \brief Some shortcuts for base types.
+ static DynNodeType decl() { return DynNodeType(NTI_Decl); }
+ static DynNodeType stmt() { return DynNodeType(NTI_Stmt); }
+ static DynNodeType nestedNameSpecifier() {
+ return DynNodeType(NTI_NestedNameSpecifier);
+ }
+ static DynNodeType nestedNameSpecifierLoc() {
+ return DynNodeType(NTI_NestedNameSpecifierLoc);
+ }
+ static DynNodeType qualType() { return DynNodeType(NTI_QualType); }
+ static DynNodeType type() { return DynNodeType(NTI_Type); }
+ static DynNodeType typeLoc() { return DynNodeType(NTI_TypeLoc); }
+
+ /// \brief Construct an identifier for T.
+ template <class T>
+ static DynNodeType getFromNodeType() {
+ return DynNodeType(TypeToTypeId<T>::Id);
+ }
+
+ /// \brief Returns \c true if \c this and \c Other represent the same type.
+ bool isSame(DynNodeType Other);
+
+ /// \brief Returns \c true if \c this is a base type of (or same as) \c Other
+ bool isBaseOf(DynNodeType Other);
+
+ /// \brief A string representation of the type.
+ StringRef asStringRef() const;
+
+private:
+ /// \brief Type ids.
+ ///
+ /// Includes all possible base and derived types.
+ enum NodeTypeId {
+ NTI_None,
+ NTI_NestedNameSpecifier,
+ NTI_NestedNameSpecifierLoc,
+ NTI_QualType,
+ NTI_TypeLoc,
+ NTI_Decl,
+#define DECL(DERIVED, BASE) NTI_##DERIVED##Decl,
+#include "clang/AST/DeclNodes.inc"
+ NTI_Stmt,
+#define STMT(DERIVED, BASE) NTI_##DERIVED,
+#include "clang/AST/StmtNodes.inc"
+ NTI_Type,
+#define TYPE(DERIVED, BASE) NTI_##DERIVED##Type,
+#include "clang/AST/TypeNodes.def"
+ NTI_NumberOfTypes
+ };
+
+ /// \brief Use getFromNodeType<T>() to construct the type.
+ DynNodeType(NodeTypeId TypeId) : TypeId(TypeId) {}
+
+ /// \brief Returns \c true if \c Base is a base type of (or same as) \c
+ /// Derived
+ static bool isBaseOf(NodeTypeId Base, NodeTypeId Derived);
+
+ /// \brief Helper meta-function to convert a type T to its enum value.
+ ///
+ /// This struct is specialized below for all known types.
+ template <class T> struct TypeToTypeId {
+ static const NodeTypeId Id = NTI_None;
+ };
+
+ /// \brief Per type info.
+ struct TypeInfo {
+ /// \brief The id of the parent type, or None if it has no parent.
+ NodeTypeId ParentId;
+ /// \brief Name of the type.
+ const char *Name;
+ };
+ static const TypeInfo AllTypeInfo[NTI_NumberOfTypes];
+
+ NodeTypeId TypeId;
+};
+
+#define TYPE_TO_TYPE_ID(Class) \
+ template <> struct DynNodeType::TypeToTypeId<Class> { \
+ static const NodeTypeId Id = NTI_##Class; \
+ };
+TYPE_TO_TYPE_ID(NestedNameSpecifier)
+TYPE_TO_TYPE_ID(NestedNameSpecifierLoc)
+TYPE_TO_TYPE_ID(QualType)
+TYPE_TO_TYPE_ID(TypeLoc)
+TYPE_TO_TYPE_ID(Decl)
+TYPE_TO_TYPE_ID(Stmt)
+TYPE_TO_TYPE_ID(Type)
+#define DECL(DERIVED, BASE) TYPE_TO_TYPE_ID(DERIVED##Decl)
+#include "clang/AST/DeclNodes.inc"
+#define STMT(DERIVED, BASE) TYPE_TO_TYPE_ID(DERIVED)
+#include "clang/AST/StmtNodes.inc"
+#define TYPE(DERIVED, BASE) TYPE_TO_TYPE_ID(DERIVED##Type)
+#include "clang/AST/TypeNodes.def"
+
/// \brief A dynamically typed AST node container.
///
/// Stores an AST node in a type safe way. This allows writing code that
@@ -57,7 +173,7 @@
/// use the pointer outside the scope of the DynTypedNode.
template <typename T>
const T *get() const {
- return BaseConverter<T>::get(Tag, Storage.buffer);
+ return BaseConverter<T>::get(NodeType, Storage.buffer);
}
/// \brief Returns a pointer that identifies the stored AST node.
@@ -71,120 +187,112 @@
/// \brief Takes care of converting from and to \c T.
template <typename T, typename EnablerT = void> struct BaseConverter;
- /// \brief Supported base node types.
- enum NodeTypeTag {
- NT_Decl,
- NT_Stmt,
- NT_NestedNameSpecifier,
- NT_NestedNameSpecifierLoc,
- NT_QualType,
- NT_Type,
- NT_TypeLoc
- } Tag;
+ DynNodeType NodeType;
/// \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 *, NestedNameSpecifier,
- NestedNameSpecifierLoc, QualType, Type,
+ llvm::AlignedCharArrayUnion<Decl *, Stmt *, NestedNameSpecifier *,
+ NestedNameSpecifierLoc *, QualType, Type *,
TypeLoc> Storage;
};
// FIXME: Pull out abstraction for the following.
template<typename T> struct DynTypedNode::BaseConverter<T,
typename llvm::enable_if<llvm::is_base_of<Decl, T> >::type> {
- static const T *get(NodeTypeTag Tag, const char Storage[]) {
- if (Tag == NT_Decl)
+ static const T *get(DynNodeType NodeType, const char Storage[]) {
+ if (DynNodeType::decl().isBaseOf(NodeType))
return dyn_cast<T>(*reinterpret_cast<Decl*const*>(Storage));
return NULL;
}
static DynTypedNode create(const Decl &Node) {
DynTypedNode Result;
- Result.Tag = NT_Decl;
+ Result.NodeType = DynNodeType::getFromNodeType<T>();
new (Result.Storage.buffer) const Decl*(&Node);
return Result;
}
};
template<typename T> struct DynTypedNode::BaseConverter<T,
typename llvm::enable_if<llvm::is_base_of<Stmt, T> >::type> {
- static const T *get(NodeTypeTag Tag, const char Storage[]) {
- if (Tag == NT_Stmt)
+ static const T *get(DynNodeType NodeType, const char Storage[]) {
+ if (DynNodeType::stmt().isBaseOf(NodeType))
return dyn_cast<T>(*reinterpret_cast<Stmt*const*>(Storage));
return NULL;
}
static DynTypedNode create(const Stmt &Node) {
DynTypedNode Result;
- Result.Tag = NT_Stmt;
+ Result.NodeType = DynNodeType::getFromNodeType<T>();
new (Result.Storage.buffer) const Stmt*(&Node);
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)
+ static const T *get(DynNodeType NodeType, const char Storage[]) {
+ if (DynNodeType::type().isBaseOf(NodeType))
return dyn_cast<T>(*reinterpret_cast<Type*const*>(Storage));
return NULL;
}
static DynTypedNode create(const Type &Node) {
DynTypedNode Result;
- Result.Tag = NT_Type;
+ Result.NodeType = DynNodeType::getFromNodeType<T>();
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)
+ static const NestedNameSpecifier *get(DynNodeType NodeType,
+ const char Storage[]) {
+ if (DynNodeType::nestedNameSpecifier().isBaseOf(NodeType))
return *reinterpret_cast<NestedNameSpecifier*const*>(Storage);
return NULL;
}
static DynTypedNode create(const NestedNameSpecifier &Node) {
DynTypedNode Result;
- Result.Tag = NT_NestedNameSpecifier;
+ Result.NodeType = DynNodeType::getFromNodeType<NestedNameSpecifier>();
new (Result.Storage.buffer) const NestedNameSpecifier*(&Node);
return Result;
}
};
template<> struct DynTypedNode::BaseConverter<NestedNameSpecifierLoc, void> {
- static const NestedNameSpecifierLoc *get(NodeTypeTag Tag,
+ static const NestedNameSpecifierLoc *get(DynNodeType NodeType,
const char Storage[]) {
- if (Tag == NT_NestedNameSpecifierLoc)
+ if (DynNodeType::nestedNameSpecifierLoc().isBaseOf(NodeType))
return reinterpret_cast<const NestedNameSpecifierLoc*>(Storage);
return NULL;
}
static DynTypedNode create(const NestedNameSpecifierLoc &Node) {
DynTypedNode Result;
- Result.Tag = NT_NestedNameSpecifierLoc;
+ Result.NodeType = DynNodeType::getFromNodeType<NestedNameSpecifierLoc>();
new (Result.Storage.buffer) NestedNameSpecifierLoc(Node);
return Result;
}
};
template<> struct DynTypedNode::BaseConverter<QualType, void> {
- static const QualType *get(NodeTypeTag Tag, const char Storage[]) {
- if (Tag == NT_QualType)
+ static const QualType *get(DynNodeType NodeType, const char Storage[]) {
+ if (DynNodeType::qualType().isBaseOf(NodeType))
return reinterpret_cast<const QualType*>(Storage);
return NULL;
}
static DynTypedNode create(const QualType &Node) {
DynTypedNode Result;
- Result.Tag = NT_QualType;
+ Result.NodeType = DynNodeType::getFromNodeType<QualType>();
new (Result.Storage.buffer) QualType(Node);
return Result;
}
};
template<> struct DynTypedNode::BaseConverter<TypeLoc, void> {
- static const TypeLoc *get(NodeTypeTag Tag, const char Storage[]) {
- if (Tag == NT_TypeLoc)
+ static const TypeLoc *get(DynNodeType NodeType, const char Storage[]) {
+ if (DynNodeType::typeLoc().isBaseOf(NodeType))
return reinterpret_cast<const TypeLoc*>(Storage);
return NULL;
}
static DynTypedNode create(const TypeLoc &Node) {
DynTypedNode Result;
- Result.Tag = NT_TypeLoc;
+ Result.NodeType = DynNodeType::getFromNodeType<TypeLoc>();
new (Result.Storage.buffer) TypeLoc(Node);
return Result;
}
@@ -194,15 +302,18 @@
// AST node that is not supported, but prevents misuse - a user cannot create
// a DynTypedNode from arbitrary types.
template <typename T, typename EnablerT> struct DynTypedNode::BaseConverter {
- static const T *get(NodeTypeTag Tag, const char Storage[]) { return NULL; }
+ static const T *get(DynNodeType NodeType, const char Storage[]) {
+ return NULL;
+ }
};
inline const void *DynTypedNode::getMemoizationData() const {
- switch (Tag) {
- case NT_Decl: return BaseConverter<Decl>::get(Tag, Storage.buffer);
- case NT_Stmt: return BaseConverter<Stmt>::get(Tag, Storage.buffer);
- default: return NULL;
- };
+ if (DynNodeType::decl().isBaseOf(NodeType)) {
+ return BaseConverter<Decl>::get(NodeType, Storage.buffer);
+ } else if (DynNodeType::stmt().isBaseOf(NodeType)) {
+ return BaseConverter<Stmt>::get(NodeType, Storage.buffer);
+ }
+ return NULL;
}
} // end namespace ast_type_traits
Index: include/clang/ASTMatchers/ASTMatchersInternal.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchersInternal.h
+++ include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -225,6 +225,8 @@
}
};
+template <typename T> class Matcher;
+
/// \brief Base class for all matchers that works on a \c DynTypedNode.
///
/// Matcher implementations will check whether the \c DynTypedNode is
@@ -244,6 +246,12 @@
/// \brief Returns a unique ID for the matcher.
virtual uint64_t getID() const = 0;
+
+ /// \brief Returns the type this matcher works on.
+ ///
+ /// \c matches() will always return false unless the node passed is of this
+ /// or a derived type.
+ virtual ast_type_traits::DynNodeType getSupportedType() const = 0;
};
/// \brief Wrapper of a MatcherInterface<T> *that allows copying.
@@ -281,6 +289,26 @@
llvm::is_same<TypeT, Type>::value >::type* = 0)
: Implementation(new TypeToQualType<TypeT>(Other)) {}
+ /// \brief Returns \c true if the pass DynTypedMatcher can be converted to \c
+ /// Matcher<T>.
+ ///
+ /// This method verifies that the underlying matcher in \c Other can process
+ /// nodes of types T.
+ static bool canConstructFrom(const DynTypedMatcher &Other) {
+ return Other.getSupportedType()
+ .isBaseOf(ast_type_traits::DynNodeType::getFromNodeType<T>());
+ }
+
+ /// \brief Construct a Matcher<T> interface around the dynamic \c Other.
+ ///
+ /// This method asserts that canConstructFrom(Other) is \c true. Callers
+ /// should call canConstructFrom(Other) first to make sure that Other is
+ /// compatible with T.
+ static Matcher<T> constructFrom(const DynTypedMatcher &Other) {
+ assert(canConstructFrom(Other));
+ return Matcher<T>(new WrappedMatcher(Other));
+ }
+
/// \brief Forwards the call to the underlying MatcherInterface<T> pointer.
bool matches(const T &Node,
ASTMatchFinder *Finder,
@@ -295,6 +323,11 @@
return reinterpret_cast<uint64_t>(Implementation.getPtr());
}
+ /// \brief Returns the type this matcher works on.
+ ast_type_traits::DynNodeType getSupportedType() const {
+ return ast_type_traits::DynNodeType::getFromNodeType<T>();
+ }
+
/// \brief Returns whether the matcher matches on the given \c DynNode.
virtual bool matches(const ast_type_traits::DynTypedNode DynNode,
ASTMatchFinder *Finder,
@@ -349,6 +382,23 @@
const Matcher<Base> From;
};
+ /// \brief Simple MatcherInterface<T> wrapper around a DynTypedMatcher.
+ class WrappedMatcher : public MatcherInterface<T> {
+ public:
+ explicit WrappedMatcher(const DynTypedMatcher &Matcher)
+ : Inner(Matcher.clone()) {}
+ virtual ~WrappedMatcher() {}
+
+ bool matches(const T &Node, ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return Inner->matches(ast_type_traits::DynTypedNode::create(Node), Finder,
+ Builder);
+ }
+
+ private:
+ const OwningPtr<DynTypedMatcher> Inner;
+ };
+
IntrusiveRefCntPtr< MatcherInterface<T> > Implementation;
}; // class Matcher
@@ -359,6 +409,31 @@
return Matcher<T>(Implementation);
}
+/// \brief Specialization of the conversion functions for QualType.
+///
+/// These specializations provide the Matcher<Type>->Matcher<QualType>
+/// conversion that the static API does.
+template <>
+inline bool
+Matcher<QualType>::canConstructFrom(const DynTypedMatcher &Other) {
+ ast_type_traits::DynNodeType SourceType = Other.getSupportedType();
+ // We support implicit conversion from Matcher<Type> to Matcher<QualType>
+ return SourceType.isSame(ast_type_traits::DynNodeType::type()) ||
+ SourceType.isSame(ast_type_traits::DynNodeType::qualType());
+}
+
+template <>
+inline Matcher<QualType>
+Matcher<QualType>::constructFrom(const DynTypedMatcher &Other) {
+ assert(canConstructFrom(Other));
+ ast_type_traits::DynNodeType SourceType = Other.getSupportedType();
+ if (SourceType.isSame(ast_type_traits::DynNodeType::type())) {
+ // We support implicit conversion from Matcher<Type> to Matcher<QualType>
+ return Matcher<Type>::constructFrom(Other);
+ }
+ return makeMatcher(new WrappedMatcher(Other));
+}
+
/// \brief Metafunction to determine if type T has a member called getDecl.
template <typename T> struct has_getDecl {
struct Default { int getDecl; };
Index: include/clang/ASTMatchers/Dynamic/VariantValue.h
===================================================================
--- include/clang/ASTMatchers/Dynamic/VariantValue.h
+++ include/clang/ASTMatchers/Dynamic/VariantValue.h
@@ -63,44 +63,24 @@
/// \brief Set the value to be \c Matcher by taking ownership of the object.
void takeMatcher(DynTypedMatcher *Matcher);
- /// \brief Specialized Matcher<T> is/get functions.
+ /// \brief Specialized Matcher<T> functions.
template <class T>
- bool isTypedMatcher() const {
- // TODO: Add some logic to test if T is actually valid for the underlying
- // type of the matcher.
- return isMatcher();
+ bool hasTypedMatcher() const {
+ return isMatcher() &&
+ ast_matchers::internal::Matcher<T>::canConstructFrom(getMatcher());
}
template <class T>
ast_matchers::internal::Matcher<T> getTypedMatcher() const {
- return ast_matchers::internal::makeMatcher(
- new DerivedTypeMatcher<T>(getMatcher()));
+ return ast_matchers::internal::Matcher<T>::constructFrom(getMatcher());
}
+ /// \brief String representation of the type of the value.
+ std::string getTypeAsString() const;
+
private:
void reset();
- /// \brief Matcher bridge between a Matcher<T> and a generic DynTypedMatcher.
- template <class T>
- class DerivedTypeMatcher :
- public ast_matchers::internal::MatcherInterface<T> {
- public:
- explicit DerivedTypeMatcher(const DynTypedMatcher &DynMatcher)
- : DynMatcher(DynMatcher.clone()) {}
- virtual ~DerivedTypeMatcher() {}
-
- typedef ast_matchers::internal::ASTMatchFinder ASTMatchFinder;
- typedef ast_matchers::internal::BoundNodesTreeBuilder BoundNodesTreeBuilder;
- bool matches(const T &Node, ASTMatchFinder *Finder,
- BoundNodesTreeBuilder *Builder) const {
- return DynMatcher->matches(ast_type_traits::DynTypedNode::create(Node),
- Finder, Builder);
- }
-
- private:
- const OwningPtr<DynTypedMatcher> DynMatcher;
- };
-
/// \brief All supported value types.
enum ValueType {
VT_Nothing,
Index: lib/AST/ASTTypeTraits.cpp
===================================================================
--- /dev/null
+++ lib/AST/ASTTypeTraits.cpp
@@ -0,0 +1,56 @@
+//===--- ASTTypeTraits.cpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Provides a dynamic type identifier and a dynamically typed node container
+// that can be used to store an AST base node at runtime in the same storage in
+// a type safe way.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTTypeTraits.h"
+
+namespace clang {
+namespace ast_type_traits {
+
+const DynNodeType::TypeInfo DynNodeType::AllTypeInfo[] = {
+ { NTI_None, "<None>" },
+ { NTI_None, "NestedNameSpecifier" },
+ { NTI_None, "NestedNameSpecifierLoc" },
+ { NTI_None, "QualType" },
+ { NTI_None, "TypeLoc" },
+ { NTI_None, "Decl" },
+#define DECL(DERIVED, BASE) { NTI_##BASE, #DERIVED "Decl" },
+#include "clang/AST/DeclNodes.inc"
+ { NTI_None, "Stmt" },
+#define STMT(DERIVED, BASE) { NTI_##BASE, #DERIVED },
+#include "clang/AST/StmtNodes.inc"
+ { NTI_None, "Type" },
+#define TYPE(DERIVED, BASE) { NTI_##BASE, #DERIVED "Type" },
+#include "clang/AST/TypeNodes.def"
+};
+
+bool DynNodeType::isBaseOf(DynNodeType Other) {
+ return isBaseOf(TypeId, Other.TypeId);
+}
+
+bool DynNodeType::isSame(DynNodeType Other) {
+ return TypeId != NTI_None && TypeId == Other.TypeId;
+}
+
+bool DynNodeType::isBaseOf(NodeTypeId Base, NodeTypeId Derived) {
+ if (Base == NTI_None || Derived == NTI_None) return false;
+ while (Derived != Base && Derived != NTI_None)
+ Derived = AllTypeInfo[Derived].ParentId;
+ return Derived == Base;
+}
+
+StringRef DynNodeType::asStringRef() const { return AllTypeInfo[TypeId].Name; }
+
+} // end namespace ast_type_traits
+} // end namespace clang
Index: lib/AST/CMakeLists.txt
===================================================================
--- lib/AST/CMakeLists.txt
+++ lib/AST/CMakeLists.txt
@@ -7,6 +7,7 @@
ASTDiagnostic.cpp
ASTDumper.cpp
ASTImporter.cpp
+ ASTTypeTraits.cpp
AttrImpl.cpp
CXXInheritance.cpp
Comment.cpp
Index: lib/ASTMatchers/ASTMatchFinder.cpp
===================================================================
--- lib/ASTMatchers/ASTMatchFinder.cpp
+++ lib/ASTMatchers/ASTMatchFinder.cpp
@@ -746,10 +746,29 @@
bool MatchFinder::addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch,
MatchCallback *Action) {
- MatcherCallbackPairs.push_back(std::make_pair(NodeMatch.clone(), Action));
- // TODO: Do runtime type checking to make sure the matcher is one of the valid
- // top-level matchers.
- return true;
+ // We only accept through this method the same types we accept through the
+ // statically typed interface. If the passed matcher is not of any of those
+ // types, we return false.
+ if (DeclarationMatcher::canConstructFrom(NodeMatch)) {
+ addMatcher(DeclarationMatcher::constructFrom(NodeMatch), Action);
+ return true;
+ } else if (TypeMatcher::canConstructFrom(NodeMatch)) {
+ addMatcher(TypeMatcher::constructFrom(NodeMatch), Action);
+ return true;
+ } else if (StatementMatcher::canConstructFrom(NodeMatch)) {
+ addMatcher(StatementMatcher::constructFrom(NodeMatch), Action);
+ return true;
+ } else if (NestedNameSpecifierMatcher::canConstructFrom(NodeMatch)) {
+ addMatcher(NestedNameSpecifierMatcher::constructFrom(NodeMatch), Action);
+ return true;
+ } else if (NestedNameSpecifierLocMatcher::canConstructFrom(NodeMatch)) {
+ addMatcher(NestedNameSpecifierLocMatcher::constructFrom(NodeMatch), Action);
+ return true;
+ } else if (TypeLocMatcher::canConstructFrom(NodeMatch)) {
+ addMatcher(TypeLocMatcher::constructFrom(NodeMatch), Action);
+ return true;
+ }
+ return false;
}
ASTConsumer *MatchFinder::newASTConsumer() {
Index: lib/ASTMatchers/Dynamic/Diagnostics.cpp
===================================================================
--- lib/ASTMatchers/Dynamic/Diagnostics.cpp
+++ lib/ASTMatchers/Dynamic/Diagnostics.cpp
@@ -36,7 +36,7 @@
case Diagnostics::ET_RegistryWrongArgCount:
return "Incorrect argument count. (Expected = $0) != (Actual = $1)";
case Diagnostics::ET_RegistryWrongArgType:
- return "Incorrect type on function $0 for arg $1.";
+ return "Incorrect type for arg $0. (Expected = $1) != (Actual = $2)";
case Diagnostics::ET_ParserStringError:
return "Error parsing string token: <$0>";
Index: lib/ASTMatchers/Dynamic/Marshallers.h
===================================================================
--- lib/ASTMatchers/Dynamic/Marshallers.h
+++ lib/ASTMatchers/Dynamic/Marshallers.h
@@ -44,14 +44,22 @@
};
template <> struct ArgTypeTraits<std::string> {
+ static StringRef asString() { return "String"; }
static bool is(const VariantValue &Value) { return Value.isString(); }
static const std::string &get(const VariantValue &Value) {
return Value.getString();
}
};
template <class T> struct ArgTypeTraits<ast_matchers::internal::Matcher<T> > {
- static bool is(const VariantValue &Value) { return Value.isMatcher(); }
+ static std::string asString() {
+ return (Twine("Matcher<") +
+ ast_type_traits::DynNodeType::getFromNodeType<T>().asStringRef() +
+ ">").str();
+ }
+ static bool is(const VariantValue &Value) {
+ return Value.hasTypedMatcher<T>();
+ }
static ast_matchers::internal::Matcher<T> get(const VariantValue &Value) {
return Value.getTypedMatcher<T>();
}
@@ -112,7 +120,8 @@
#define CHECK_ARG_TYPE(index, type) \
if (!ArgTypeTraits<type>::is(Args[index].Value)) { \
Error->pushErrorFrame(Args[index].Range, Error->ET_RegistryWrongArgType) \
- << MatcherName << (index + 1); \
+ << (index + 1) << ArgTypeTraits<type>::asString() \
+ << Args[index].Value.getTypeAsString(); \
return NULL; \
}
Index: lib/ASTMatchers/Dynamic/VariantValue.cpp
===================================================================
--- lib/ASTMatchers/Dynamic/VariantValue.cpp
+++ lib/ASTMatchers/Dynamic/VariantValue.cpp
@@ -14,6 +14,8 @@
#include "clang/ASTMatchers/Dynamic/VariantValue.h"
+#include "clang/Basic/LLVM.h"
+
namespace clang {
namespace ast_matchers {
namespace dynamic {
@@ -100,6 +102,17 @@
Value.Matcher = NewValue;
}
+std::string VariantValue::getTypeAsString() const {
+ switch (Type) {
+ case VT_String: return "String";
+ case VT_Matcher:
+ return (Twine("Matcher<") + getMatcher().getSupportedType().asStringRef() +
+ ">").str();
+ case VT_Nothing: return "Nothing";
+ }
+ llvm_unreachable("Invalid Type");
+}
+
} // end namespace dynamic
} // end namespace ast_matchers
} // end namespace clang
Index: unittests/AST/ASTTypeTraitsTest.cpp
===================================================================
--- /dev/null
+++ unittests/AST/ASTTypeTraitsTest.cpp
@@ -0,0 +1,62 @@
+//===- unittest/AST/ASTTypeTraits.cpp - AST type traits unit tests ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+
+
+#include "clang/AST/ASTTypeTraits.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace ast_type_traits {
+
+TEST(DynNodeType, NoType) {
+ EXPECT_FALSE(DynNodeType().isBaseOf(DynNodeType()));
+ EXPECT_FALSE(DynNodeType().isSame(DynNodeType()));
+}
+
+template <typename T> static DynNodeType DNT() {
+ return DynNodeType::getFromNodeType<T>();
+}
+
+TEST(DynNodeType, Bases) {
+ EXPECT_TRUE(DynNodeType::decl().isBaseOf(DNT<VarDecl>()));
+ EXPECT_FALSE(DynNodeType::decl().isSame(DNT<VarDecl>()));
+ EXPECT_FALSE(DNT<VarDecl>().isBaseOf(DynNodeType::decl()));
+
+ EXPECT_TRUE(DynNodeType::decl().isSame(DNT<Decl>()));
+}
+
+TEST(DynNodeType, SameBase) {
+ EXPECT_TRUE(DNT<Expr>().isBaseOf(DNT<CallExpr>()));
+ EXPECT_TRUE(DNT<Expr>().isBaseOf(DNT<BinaryOperator>()));
+ EXPECT_FALSE(DNT<CallExpr>().isBaseOf(DNT<BinaryOperator>()));
+ EXPECT_FALSE(DNT<BinaryOperator>().isBaseOf(DNT<CallExpr>()));
+}
+
+TEST(DynNodeType, DiffBase) {
+ EXPECT_FALSE(DNT<Expr>().isBaseOf(DNT<ArrayType>()));
+ EXPECT_FALSE(DNT<QualType>().isBaseOf(DNT<FunctionDecl>()));
+ EXPECT_FALSE(DNT<Type>().isSame(DNT<QualType>()));
+}
+
+struct Foo {};
+
+TEST(DynNodeType, UnknownType) {
+ // We can construct one, but it is nowhere in the hierarchy.
+ EXPECT_FALSE(DNT<Foo>().isSame(DNT<Foo>()));
+}
+
+TEST(DynNodeType, Name) {
+ EXPECT_EQ("Decl", DNT<Decl>().asStringRef());
+ EXPECT_EQ("CallExpr", DNT<CallExpr>().asStringRef());
+ EXPECT_EQ("ConstantArrayType", DNT<ConstantArrayType>().asStringRef());
+ EXPECT_EQ("<None>", DynNodeType().asStringRef());
+}
+
+} // namespace ast_type_traits
+} // namespace clang
Index: unittests/AST/CMakeLists.txt
===================================================================
--- unittests/AST/CMakeLists.txt
+++ unittests/AST/CMakeLists.txt
@@ -1,5 +1,6 @@
add_clang_unittest(ASTTests
ASTContextParentMapTest.cpp
+ ASTTypeTraitsTest.cpp
CommentLexer.cpp
CommentParser.cpp
DeclPrinterTest.cpp
Index: unittests/ASTMatchers/ASTMatchersTest.cpp
===================================================================
--- unittests/ASTMatchers/ASTMatchersTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTest.cpp
@@ -40,6 +40,18 @@
}
#endif
+TEST(Finder, DynamicOnlyAcceptsSomeMatchers) {
+ MatchFinder Finder;
+ EXPECT_TRUE(Finder.addDynamicMatcher(decl(), NULL));
+ EXPECT_TRUE(Finder.addDynamicMatcher(callExpr(), NULL));
+ EXPECT_TRUE(Finder.addDynamicMatcher(constantArrayType(hasSize(42)), NULL));
+
+ // Do not accept non-toplevel matchers.
+ EXPECT_FALSE(Finder.addDynamicMatcher(isArrow(), NULL));
+ EXPECT_FALSE(Finder.addDynamicMatcher(hasSize(2), NULL));
+ EXPECT_FALSE(Finder.addDynamicMatcher(hasName("x"), NULL));
+}
+
TEST(Decl, MatchesDeclarations) {
EXPECT_TRUE(notMatches("", decl(usingDecl())));
EXPECT_TRUE(matches("namespace x { class X {}; } using x::X;",
Index: unittests/ASTMatchers/Dynamic/ParserTest.cpp
===================================================================
--- unittests/ASTMatchers/Dynamic/ParserTest.cpp
+++ unittests/ASTMatchers/Dynamic/ParserTest.cpp
@@ -41,6 +41,10 @@
/// \brief Returns a unique ID for the matcher.
virtual uint64_t getID() const { return ID; }
+ virtual ast_type_traits::DynNodeType getSupportedType() const {
+ return ast_type_traits::DynNodeType();
+ }
+
private:
uint64_t ID;
};
@@ -142,7 +146,8 @@
TEST(ParserTest, FullParserTest) {
OwningPtr<DynTypedMatcher> Matcher(Parser::parseMatcherExpression(
- "hasInitializer(binaryOperator(hasLHS(integerLiteral())))", NULL));
+ "varDecl(hasInitializer(binaryOperator(hasLHS(integerLiteral()))))",
+ NULL));
EXPECT_TRUE(matchesDynamic("int x = 1 + false;", *Matcher));
EXPECT_FALSE(matchesDynamic("int x = true + 1;", *Matcher));
@@ -152,7 +157,8 @@
EXPECT_EQ("1:1: Error parsing argument 1 for matcher hasInitializer.\n"
"2:5: Error parsing argument 1 for matcher binaryOperator.\n"
"2:20: Error building matcher hasLHS.\n"
- "2:27: Incorrect type on function hasLHS for arg 1.",
+ "2:27: Incorrect type for arg 1. "
+ "(Expected = Matcher<Expr>) != (Actual = String)",
Error.ToStringFull());
}
Index: unittests/ASTMatchers/Dynamic/RegistryTest.cpp
===================================================================
--- unittests/ASTMatchers/Dynamic/RegistryTest.cpp
+++ unittests/ASTMatchers/Dynamic/RegistryTest.cpp
@@ -20,32 +20,55 @@
using ast_matchers::internal::Matcher;
-DynTypedMatcher *constructMatcher(StringRef MatcherName, Diagnostics *Error) {
- const std::vector<ParserValue> Args;
- return Registry::constructMatcher(MatcherName, SourceRange(), Args, Error);
-}
-
-DynTypedMatcher *constructMatcher(StringRef MatcherName,
- const VariantValue &Arg1,
- Diagnostics *Error) {
- std::vector<ParserValue> Args(1);
- Args[0].Value = Arg1;
- return Registry::constructMatcher(MatcherName, SourceRange(), Args, Error);
-}
-
-DynTypedMatcher *constructMatcher(StringRef MatcherName,
- const VariantValue &Arg1,
- const VariantValue &Arg2,
- Diagnostics *Error) {
- std::vector<ParserValue> Args(2);
- Args[0].Value = Arg1;
- Args[1].Value = Arg2;
- return Registry::constructMatcher(MatcherName, SourceRange(), Args, Error);
-}
-
-TEST(RegistryTest, CanConstructNoArgs) {
- OwningPtr<DynTypedMatcher> IsArrowValue(constructMatcher("isArrow", NULL));
- OwningPtr<DynTypedMatcher> BoolValue(constructMatcher("boolLiteral", NULL));
+class RegistryTest : public ::testing::Test {
+public:
+ ~RegistryTest() {
+ for (size_t I = 0, E = ToDelete.size(); I != E; ++I) {
+ delete ToDelete[I];
+ }
+ }
+
+ const DynTypedMatcher *constructMatcher(StringRef MatcherName,
+ Diagnostics *Error) {
+ const std::vector<ParserValue> Args;
+ DynTypedMatcher *Out =
+ Registry::constructMatcher(MatcherName, SourceRange(), Args, Error);
+ ToDelete.push_back(Out);
+ return Out;
+ }
+
+ const DynTypedMatcher *constructMatcher(StringRef MatcherName,
+ const VariantValue &Arg1,
+ Diagnostics *Error) {
+ std::vector<ParserValue> Args(1);
+ Args[0].Value = Arg1;
+ DynTypedMatcher *Out =
+ Registry::constructMatcher(MatcherName, SourceRange(), Args, Error);
+ ToDelete.push_back(Out);
+ return Out;
+ }
+
+ const DynTypedMatcher *constructMatcher(StringRef MatcherName,
+ const VariantValue &Arg1,
+ const VariantValue &Arg2,
+ Diagnostics *Error) {
+ std::vector<ParserValue> Args(2);
+ Args[0].Value = Arg1;
+ Args[1].Value = Arg2;
+ DynTypedMatcher *Out =
+ Registry::constructMatcher(MatcherName, SourceRange(), Args, Error);
+ ToDelete.push_back(Out);
+ return Out;
+ }
+
+private:
+ std::vector<DynTypedMatcher *> ToDelete;
+};
+
+TEST_F(RegistryTest, CanConstructNoArgs) {
+ const DynTypedMatcher *IsArrowValue =
+ constructMatcher("memberExpr", *constructMatcher("isArrow", NULL), NULL);
+ const DynTypedMatcher *BoolValue = constructMatcher("boolLiteral", NULL);
const std::string ClassSnippet = "struct Foo { int x; };\n"
"Foo *foo = new Foo;\n"
@@ -58,18 +81,18 @@
EXPECT_FALSE(matchesDynamic(BoolSnippet, *IsArrowValue));
}
-TEST(RegistryTest, ConstructWithSimpleArgs) {
- OwningPtr<DynTypedMatcher> Value(
- constructMatcher("hasName", std::string("X"), NULL));
+TEST_F(RegistryTest, ConstructWithSimpleArgs) {
+ const DynTypedMatcher *Value = constructMatcher(
+ "namedDecl", *constructMatcher("hasName", std::string("X"), NULL), NULL);
EXPECT_TRUE(matchesDynamic("class X {};", *Value));
EXPECT_FALSE(matchesDynamic("int x;", *Value));
}
-TEST(RegistryTest, ConstructWithMatcherArgs) {
- OwningPtr<DynTypedMatcher> HasInitializerSimple(
- constructMatcher("hasInitializer", stmt(), NULL));
- OwningPtr<DynTypedMatcher> HasInitializerComplex(
- constructMatcher("hasInitializer", callExpr(), NULL));
+TEST_F(RegistryTest, ConstructWithMatcherArgs) {
+ const DynTypedMatcher *HasInitializerSimple = constructMatcher(
+ "varDecl", *constructMatcher("hasInitializer", stmt(), NULL), NULL);
+ const DynTypedMatcher *HasInitializerComplex = constructMatcher(
+ "varDecl", *constructMatcher("hasInitializer", callExpr(), NULL), NULL);
std::string code = "int i;";
EXPECT_FALSE(matchesDynamic(code, *HasInitializerSimple));
@@ -84,7 +107,7 @@
EXPECT_TRUE(matchesDynamic(code, *HasInitializerComplex));
}
-TEST(RegistryTest, Errors) {
+TEST_F(RegistryTest, Errors) {
// Incorrect argument count.
OwningPtr<Diagnostics> Error(new Diagnostics());
EXPECT_TRUE(NULL == constructMatcher("hasInitializer", Error.get()));
@@ -98,11 +121,14 @@
// Bad argument type
Error.reset(new Diagnostics());
EXPECT_TRUE(NULL == constructMatcher("ofClass", std::string(), Error.get()));
- EXPECT_EQ("Incorrect type on function ofClass for arg 1.", Error->ToString());
+ EXPECT_EQ("Incorrect type for arg 1. (Expected = Matcher<CXXRecordDecl>) != "
+ "(Actual = String)",
+ Error->ToString());
Error.reset(new Diagnostics());
EXPECT_TRUE(NULL == constructMatcher("recordDecl", recordDecl(),
- ::std::string(), Error.get()));
- EXPECT_EQ("Incorrect type on function recordDecl for arg 2.",
+ parameterCountIs(3), Error.get()));
+ EXPECT_EQ("Incorrect type for arg 2. (Expected = Matcher<CXXRecordDecl>) != "
+ "(Actual = Matcher<FunctionDecl>)",
Error->ToString());
}
Index: unittests/ASTMatchers/Dynamic/VariantValueTest.cpp
===================================================================
--- unittests/ASTMatchers/Dynamic/VariantValueTest.cpp
+++ unittests/ASTMatchers/Dynamic/VariantValueTest.cpp
@@ -25,51 +25,58 @@
EXPECT_TRUE(Value.isString());
EXPECT_EQ(kString, Value.getString());
+ EXPECT_EQ("String", Value.getTypeAsString());
EXPECT_FALSE(Value.isMatcher());
- EXPECT_FALSE(Value.isTypedMatcher<clang::Decl>());
- EXPECT_FALSE(Value.isTypedMatcher<clang::UnaryOperator>());
+ EXPECT_FALSE(Value.hasTypedMatcher<clang::Decl>());
+ EXPECT_FALSE(Value.hasTypedMatcher<clang::UnaryOperator>());
}
TEST(VariantValueTest, DynTypedMatcher) {
VariantValue Value = stmt();
EXPECT_FALSE(Value.isString());
EXPECT_TRUE(Value.isMatcher());
- EXPECT_TRUE(Value.isTypedMatcher<clang::Decl>());
- EXPECT_TRUE(Value.isTypedMatcher<clang::UnaryOperator>());
+ EXPECT_FALSE(Value.hasTypedMatcher<clang::Decl>());
+ EXPECT_TRUE(Value.hasTypedMatcher<clang::UnaryOperator>());
+ EXPECT_EQ("Matcher<Stmt>", Value.getTypeAsString());
- // Conversion to any type of matcher works.
- // If they are not compatible it would just return a matcher that matches
- // nothing. We test this below.
+ // Can only convert to compatible matchers.
Value = recordDecl();
EXPECT_TRUE(Value.isMatcher());
- EXPECT_TRUE(Value.isTypedMatcher<clang::Decl>());
- EXPECT_TRUE(Value.isTypedMatcher<clang::UnaryOperator>());
+ EXPECT_TRUE(Value.hasTypedMatcher<clang::Decl>());
+ EXPECT_FALSE(Value.hasTypedMatcher<clang::UnaryOperator>());
+ EXPECT_EQ("Matcher<Decl>", Value.getTypeAsString());
- Value = unaryOperator();
+ Value = ignoringImpCasts(expr());
EXPECT_TRUE(Value.isMatcher());
- EXPECT_TRUE(Value.isTypedMatcher<clang::Decl>());
- EXPECT_TRUE(Value.isTypedMatcher<clang::Stmt>());
- EXPECT_TRUE(Value.isTypedMatcher<clang::UnaryOperator>());
+ 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_EQ("Matcher<Expr>", Value.getTypeAsString());
}
TEST(VariantValueTest, Assignment) {
VariantValue Value = std::string("A");
EXPECT_TRUE(Value.isString());
EXPECT_EQ("A", Value.getString());
EXPECT_FALSE(Value.isMatcher());
+ EXPECT_EQ("String", Value.getTypeAsString());
Value = recordDecl();
EXPECT_FALSE(Value.isString());
EXPECT_TRUE(Value.isMatcher());
- EXPECT_TRUE(Value.isTypedMatcher<clang::Decl>());
- EXPECT_TRUE(Value.isTypedMatcher<clang::UnaryOperator>());
+ EXPECT_TRUE(Value.hasTypedMatcher<clang::Decl>());
+ EXPECT_FALSE(Value.hasTypedMatcher<clang::UnaryOperator>());
+ EXPECT_EQ("Matcher<Decl>", Value.getTypeAsString());
Value = VariantValue();
EXPECT_FALSE(Value.isString());
EXPECT_FALSE(Value.isMatcher());
+ EXPECT_EQ("Nothing", Value.getTypeAsString());
}
TEST(GeneicValueTest, Matcher) {
@@ -79,10 +86,13 @@
"int x;", VariantValue(varDecl()).getTypedMatcher<clang::Decl>()));
EXPECT_TRUE(matchesDynamic("int foo() { return 1 + 1; }",
VariantValue(functionDecl()).getMatcher()));
- // Going through the wrong Matcher<T> will fail to match, even if the
- // underlying matcher is correct.
- EXPECT_FALSE(matchesDynamic(
- "int x;", VariantValue(varDecl()).getTypedMatcher<clang::Stmt>()));
+ // Can't get the wrong matcher.
+ EXPECT_FALSE(VariantValue(varDecl()).hasTypedMatcher<clang::Stmt>());
+#if GTEST_HAS_DEATH_TEST
+ // Trying to get the wrong matcher fails an assertion in Matcher<T>.
+ EXPECT_DEBUG_DEATH(VariantValue(varDecl()).getTypedMatcher<clang::Stmt>(),
+ "canConstructFrom");
+#endif
EXPECT_FALSE(
matchesDynamic("int x;", VariantValue(functionDecl()).getMatcher()));
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits