Index: include/clang/ASTMatchers/ASTMatchFinder.h =================================================================== --- include/clang/ASTMatchers/ASTMatchFinder.h +++ include/clang/ASTMatchers/ASTMatchFinder.h @@ -95,6 +95,10 @@ virtual void run() = 0; }; + /// \brief The \c MatchCallback*'s will be called every time the + /// \c DynTypedMatcher matches on the AST. + typedef std::pair Trigger; + MatchFinder(); ~MatchFinder(); @@ -125,11 +129,8 @@ void registerTestCallbackAfterParsing(ParsingDoneTestCallback *ParsingDone); private: - /// \brief The MatchCallback*'s will be called every time the - /// UntypedBaseMatcher matches on the AST. - std::vector< std::pair< - const internal::UntypedBaseMatcher*, - MatchCallback*> > Triggers; + /// \brief The triggers executed against the AST. + std::vector Triggers; /// \brief Called when parsing is done. ParsingDoneTestCallback *ParsingDone; Index: include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- include/clang/ASTMatchers/ASTMatchers.h +++ include/clang/ASTMatchers/ASTMatchers.h @@ -68,7 +68,7 @@ /// Returns NULL if there was no node bound to \c ID or if there is a node but /// it cannot be converted to the specified type. template - const T getNodeAs(StringRef ID) const { + const T *getNodeAs(StringRef ID) const { return MyBoundNodes.getNodeAs(ID); } @@ -76,11 +76,11 @@ /// @{ template const T *getDeclAs(StringRef ID) const { - return getNodeAs(ID); + return getNodeAs(ID); } template const T *getStmtAs(StringRef ID) const { - return getNodeAs(ID); + return getNodeAs(ID); } /// @} Index: include/clang/ASTMatchers/ASTMatchersInternal.h =================================================================== --- include/clang/ASTMatchers/ASTMatchersInternal.h +++ include/clang/ASTMatchers/ASTMatchersInternal.h @@ -68,7 +68,7 @@ /// The node's base type should be in NodeBaseType or it will be unaccessible. template void addNode(StringRef ID, const T* Node) { - NodeMap[ID] = ast_type_traits::DynTypedNode::create(Node); + NodeMap[ID] = ast_type_traits::DynTypedNode::create(*Node); } void addNode(StringRef ID, ast_type_traits::DynTypedNode Node) { NodeMap[ID] = Node; @@ -79,7 +79,7 @@ /// Returns NULL if there was no node bound to \c ID or if there is a node but /// it cannot be converted to the specified type. template - const T getNodeAs(StringRef ID) const { + const T *getNodeAs(StringRef ID) const { IDToNodeMap::const_iterator It = NodeMap.find(ID); if (It == NodeMap.end()) { return NULL; @@ -205,7 +205,8 @@ BoundNodesTreeBuilder *Builder) const = 0; }; -/// \brief Interface for matchers that only evaluate properties on a single node. +/// \brief Interface for matchers that only evaluate properties on a single +/// node. template class SingleNodeMatcherInterface : public MatcherInterface { public: @@ -223,6 +224,24 @@ } }; +/// \brief Base class for all matchers that works on a \c DynTypedNode. +/// +/// Matcher implementations will check whether the \c DynTypedNode is +/// convertible into the respecitve types and then do the actual match +/// on the actual node, or return false if it is not convertible. +class DynTypedMatcher { +public: + virtual ~DynTypedMatcher() {} + + /// \brief Returns true if the matcher matches the given \c DynNode. + virtual bool matches(const ast_type_traits::DynTypedNode DynNode, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const = 0; + + /// \brief Returns a unique ID for the matcher. + virtual uint64_t getID() const = 0; +}; + /// \brief Wrapper of a MatcherInterface *that allows copying. /// /// A Matcher can be used anywhere a Matcher is @@ -232,7 +251,7 @@ /// operator rather than a type hierarchy to be able to templatize the /// type hierarchy instead of spelling it out. template -class Matcher { +class Matcher : public DynTypedMatcher { public: /// \brief Takes ownership of the provided implementation pointer. explicit Matcher(MatcherInterface *Implementation) @@ -260,6 +279,15 @@ return reinterpret_cast(Implementation.getPtr()); } + /// \brief Returns whether the matcher matches on the given \c DynNode. + virtual bool matches(const ast_type_traits::DynTypedNode DynNode, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + const T *Node = DynNode.get(); + if (!Node) return false; + return matches(*Node, Finder, Builder); + } + private: /// \brief Allows conversion from Matcher to Matcher if Derived /// is derived from T. @@ -356,65 +384,6 @@ template const bool IsBaseType::value; -/// \brief Interface that can match any AST base node type and contains default -/// implementations returning false. -class UntypedBaseMatcher : public llvm::RefCountedBaseVPTR { -public: - virtual ~UntypedBaseMatcher() {} - - virtual bool matches(const Decl &DeclNode, ASTMatchFinder *Finder, - BoundNodesTreeBuilder *Builder) const { - return false; - } - virtual bool matches(const QualType &TypeNode, ASTMatchFinder *Finder, - BoundNodesTreeBuilder *Builder) const { - return false; - } - virtual bool matches(const Stmt &StmtNode, ASTMatchFinder *Finder, - BoundNodesTreeBuilder *Builder) const { - return false; - } - virtual bool matches(const CXXCtorInitializer &CtorInitNode, - ASTMatchFinder *Finder, - BoundNodesTreeBuilder *Builder) const { - return false; - } - - /// \brief Returns a unique ID for the matcher. - virtual uint64_t getID() const = 0; -}; - -/// \brief An UntypedBaseMatcher that overwrites the Matches(...) method for -/// node type T. T must be an AST base type. -template -class TypedBaseMatcher : public UntypedBaseMatcher { - TOOLING_COMPILE_ASSERT(IsBaseType::value, - typed_base_matcher_can_only_be_used_with_base_type); -public: - explicit TypedBaseMatcher(const Matcher &InnerMatcher) - : InnerMatcher(InnerMatcher) {} - - using UntypedBaseMatcher::matches; - /// \brief Implements UntypedBaseMatcher::Matches. - /// - /// Since T is guaranteed to be a "base" AST node type, this method is - /// guaranteed to override one of the matches() methods from - /// UntypedBaseMatcher. - virtual bool matches(const T &Node, - ASTMatchFinder *Finder, - BoundNodesTreeBuilder *Builder) const { - return InnerMatcher.matches(Node, Finder, Builder); - } - - /// \brief Implements UntypedBaseMatcher::getID. - virtual uint64_t getID() const { - return InnerMatcher.getID(); - } - -private: - Matcher InnerMatcher; -}; - /// \brief Interface that allows matchers to traverse the AST. /// FIXME: Find a better name. /// @@ -454,24 +423,41 @@ const Matcher &Base, BoundNodesTreeBuilder *Builder) = 0; + template + bool matchesChildOf(const T &Node, + const DynTypedMatcher &Matcher, + BoundNodesTreeBuilder *Builder, + TraversalKind Traverse, + BindKind Bind) { + TOOLING_COMPILE_ASSERT((llvm::is_base_of::value || + llvm::is_base_of::value), + only_Decl_or_Stmt_allowed_for_recursive_matching); + return matchesChildOf(ast_type_traits::DynTypedNode::create(Node), + Matcher, Builder, Traverse, Bind); + } + + template + bool matchesDescendantOf(const T &Node, + const DynTypedMatcher &Matcher, + BoundNodesTreeBuilder *Builder, + BindKind Bind) { + TOOLING_COMPILE_ASSERT((llvm::is_base_of::value || + llvm::is_base_of::value), + only_Decl_or_Stmt_allowed_for_recursive_matching); + return matchesDescendantOf(ast_type_traits::DynTypedNode::create(Node), + Matcher, Builder, Bind); + } + +protected: // FIXME: Implement for other base nodes. - virtual bool matchesChildOf(const Decl &DeclNode, - const UntypedBaseMatcher &BaseMatcher, - BoundNodesTreeBuilder *Builder, - TraversalKind Traverse, - BindKind Bind) = 0; - virtual bool matchesChildOf(const Stmt &StmtNode, - const UntypedBaseMatcher &BaseMatcher, + virtual bool matchesChildOf(const ast_type_traits::DynTypedNode &Node, + const DynTypedMatcher &Matcher, BoundNodesTreeBuilder *Builder, TraversalKind Traverse, BindKind Bind) = 0; - virtual bool matchesDescendantOf(const Decl &DeclNode, - const UntypedBaseMatcher &BaseMatcher, - BoundNodesTreeBuilder *Builder, - BindKind Bind) = 0; - virtual bool matchesDescendantOf(const Stmt &StmtNode, - const UntypedBaseMatcher &BaseMatcher, + virtual bool matchesDescendantOf(const ast_type_traits::DynTypedNode &Node, + const DynTypedMatcher &Matcher, BoundNodesTreeBuilder *Builder, BindKind Bind) = 0; }; @@ -671,7 +657,7 @@ } private: - const TypedBaseMatcher ChildMatcher; + const Matcher ChildMatcher; }; /// \brief Matches nodes of type T that have child nodes of type ChildT for @@ -697,7 +683,7 @@ } private: - const TypedBaseMatcher ChildMatcher; + const Matcher ChildMatcher; }; /// \brief Matches nodes of type T if the given Matcher does not match. @@ -811,7 +797,7 @@ } private: - const TypedBaseMatcher DescendantMatcher; + const Matcher DescendantMatcher; }; /// \brief Matches nodes of type T that have at least one descendant node of @@ -837,7 +823,7 @@ } private: - const TypedBaseMatcher DescendantMatcher; + const Matcher DescendantMatcher; }; /// \brief Matches on nodes that have a getValue() method if getValue() equals Index: include/clang/ASTMatchers/ASTTypeTraits.h =================================================================== --- include/clang/ASTMatchers/ASTTypeTraits.h +++ include/clang/ASTMatchers/ASTTypeTraits.h @@ -17,24 +17,28 @@ #include "clang/AST/Decl.h" #include "clang/AST/Stmt.h" +#include "llvm/Support/AlignOf.h" namespace clang { namespace ast_type_traits { /// \brief A dynamically typed AST node container. /// -/// Stores an AST node in a type safe way. +/// Stores an AST node in a type safe way. This allows writing code that +/// works with different kinds AST nodes, despite the fact that they don't +/// have a common base class. +/// /// Use \c create(Node) to create a \c DynTypedNode from an AST node, /// and \c get() to retrieve the node as type T if the types match. +/// +/// See \c NodeTypeTag for which node base types are currently supported; +/// You can create DynTypedNodes for all nodes in the inheritance hierarchy of +/// the supported base types. class DynTypedNode { public: - /// \brief Creates a NULL-node, which is needed to be able to use - /// \c DynTypedNodes in STL data structures. - DynTypedNode() : Tag(), Node(NULL) {} - /// \brief Creates a \c DynTypedNode from \c Node. template - static DynTypedNode create(T Node) { + static DynTypedNode create(const T &Node) { return BaseConverter::create(Node); } @@ -42,10 +46,30 @@ /// /// Returns NULL if the stored node does not have a type that is /// convertible to \c T. + /// + /// For types that have identity via their pointer in the AST + /// (like Stmt and Decl) the returned pointer points to the + /// referenced AST node. + /// For other types (like QualType) the value is stored directly + /// in the DynTypedNode, and the returned pointer points at + /// the storage inside DynTypedNode. For those nodes, do not + /// use the pointer outside the scope of the DynTypedNode. template - T get() const { - return llvm::dyn_cast::type>( - BaseConverter::get(Tag, Node)); + const T *get() const { + return BaseConverter::get(Tag, Storage.buffer); + } + + /// \brief Returns a pointer that identifies the stored AST node. + /// + /// Note that this is not supported by all AST nodes. For AST nodes + /// that don't have a pointer-defined identity inside the AST, this + /// method returns NULL. + void *getMemoizationData() const { + switch (Tag) { + case NT_Decl: return *reinterpret_cast(Storage.buffer); + case NT_Stmt: return *reinterpret_cast(Storage.buffer); + default: return NULL; + }; } private: @@ -55,42 +79,65 @@ /// \brief Supported base node types. enum NodeTypeTag { NT_Decl, - NT_Stmt + NT_Stmt, + NT_QualType } Tag; /// \brief Stores the data of the node. - // FIXME: We really want to store a union, as we want to support - // storing TypeLoc nodes by-value. - // FIXME: Add QualType storage: we'll want to use QualType::getAsOpaquePtr() - // and getFromOpaquePtr(...) to convert to and from void*, but return the - // QualType objects by value. - void *Node; - - DynTypedNode(NodeTypeTag Tag, const void *Node) - : Tag(Tag), Node(const_cast(Node)) {} + /// + /// Note that we store the QualType by value as we only get + /// it by-value from the AST. + llvm::AlignedCharArrayUnion Storage; }; template struct DynTypedNode::BaseConverter::type > >::type > { - static Decl *get(NodeTypeTag Tag, void *Node) { - if (Tag == NT_Decl) return static_cast(Node); + typename llvm::enable_if >::type> { + static const T *get(NodeTypeTag Tag, const char Storage[]) { + if (Tag == NT_Decl) + return dyn_cast(*reinterpret_cast(Storage)); return NULL; } - static DynTypedNode create(const Decl *Node) { - return DynTypedNode(NT_Decl, Node); + static DynTypedNode create(const Decl &Node) { + DynTypedNode Result; + Result.Tag = NT_Decl; + new (Result.Storage.buffer) const Decl*(&Node); + return Result; } }; template struct DynTypedNode::BaseConverter::type > >::type > { - static Stmt *get(NodeTypeTag Tag, void *Node) { - if (Tag == NT_Stmt) return static_cast(Node); + typename llvm::enable_if >::type> { + static const T *get(NodeTypeTag Tag, const char Storage[]) { + if (Tag == NT_Stmt) + return dyn_cast(*reinterpret_cast(Storage)); return NULL; } - static DynTypedNode create(const Stmt *Node) { - return DynTypedNode(NT_Stmt, Node); + static DynTypedNode create(const Stmt &Node) { + DynTypedNode Result; + Result.Tag = NT_Stmt; + new (Result.Storage.buffer) const Stmt*(&Node); + return Result; } }; +template<> struct DynTypedNode::BaseConverter { + static const QualType *get(NodeTypeTag Tag, const char Storage[]) { + if (Tag == NT_QualType) + return reinterpret_cast(Storage); + return NULL; + } + static DynTypedNode create(const QualType &Node) { + DynTypedNode Result; + Result.Tag = NT_QualType; + new (Result.Storage.buffer) QualType(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 +// a DynTypedNode from arbitrary types. +template struct DynTypedNode::BaseConverter { + static const T *get(NodeTypeTag Tag, const char Storage[]) { return NULL; } +}; + } // end namespace ast_type_traits } // end namespace clang Index: lib/ASTMatchers/ASTMatchFinder.cpp =================================================================== --- lib/ASTMatchers/ASTMatchFinder.cpp +++ lib/ASTMatchers/ASTMatchFinder.cpp @@ -50,13 +50,13 @@ // descendants of a traversed node. max_depth is the maximum depth // to traverse: use 1 for matching the children and INT_MAX for // matching the descendants. - MatchChildASTVisitor(const UntypedBaseMatcher *BaseMatcher, + MatchChildASTVisitor(const DynTypedMatcher *Matcher, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder, int MaxDepth, ASTMatchFinder::TraversalKind Traversal, ASTMatchFinder::BindKind Bind) - : BaseMatcher(BaseMatcher), + : Matcher(Matcher), Finder(Finder), Builder(Builder), CurrentDepth(-1), @@ -76,10 +76,13 @@ // Traverse*(c) for each child c of 'node'. // - Traverse*(c) in turn calls Traverse(c), completing the // recursion. - template - bool findMatch(const T &Node) { + bool findMatch(const ast_type_traits::DynTypedNode &DynNode) { reset(); - traverse(Node); + if (const Decl *D = DynNode.get()) + traverse(*D); + else if (const Stmt *S = DynNode.get()) + traverse(*S); + // FIXME: Add other base types after adding tests. return Matches; } @@ -148,7 +151,8 @@ return baseTraverse(Node); } if (Bind != ASTMatchFinder::BK_All) { - if (BaseMatcher->matches(Node, Finder, Builder)) { + if (Matcher->matches(ast_type_traits::DynTypedNode::create(Node), + Finder, Builder)) { Matches = true; return false; // Abort as soon as a match is found. } @@ -163,7 +167,8 @@ return true; } else { BoundNodesTreeBuilder RecursiveBuilder; - if (BaseMatcher->matches(Node, Finder, &RecursiveBuilder)) { + if (Matcher->matches(ast_type_traits::DynTypedNode::create(Node), + Finder, &RecursiveBuilder)) { // After the first match the matcher succeeds. Matches = true; Builder->addMatch(RecursiveBuilder.build()); @@ -176,7 +181,7 @@ } } - const UntypedBaseMatcher *const BaseMatcher; + const DynTypedMatcher *const Matcher; ASTMatchFinder *const Finder; BoundNodesTreeBuilder *const Builder; int CurrentDepth; @@ -191,8 +196,7 @@ class MatchASTVisitor : public RecursiveASTVisitor, public ASTMatchFinder { public: - MatchASTVisitor(std::vector< std::pair > *Triggers) + MatchASTVisitor(std::vector *Triggers) : Triggers(Triggers), ActiveASTContext(NULL) { } @@ -245,21 +249,19 @@ bool TraverseTypeLoc(TypeLoc TypeNode); // Matches children or descendants of 'Node' with 'BaseMatcher'. - template - bool memoizedMatchesRecursively(const T &Node, - const UntypedBaseMatcher &BaseMatcher, + bool memoizedMatchesRecursively(const ast_type_traits::DynTypedNode &Node, + const DynTypedMatcher &Matcher, BoundNodesTreeBuilder *Builder, int MaxDepth, TraversalKind Traversal, BindKind Bind) { - TOOLING_COMPILE_ASSERT((llvm::is_same::value) || - (llvm::is_same::value), - type_does_not_support_memoization); - const UntypedMatchInput input(BaseMatcher.getID(), &Node); + const UntypedMatchInput input(Matcher.getID(), Node.getMemoizationData()); + assert(input.second && + "Fix getMemoizationData once more types allow recursive matching."); std::pair InsertResult = ResultCache.insert(std::make_pair(input, MemoizedMatchResult())); if (InsertResult.second) { BoundNodesTreeBuilder DescendantBoundNodesBuilder; InsertResult.first->second.ResultOfMatch = - matchesRecursively(Node, BaseMatcher, &DescendantBoundNodesBuilder, + matchesRecursively(Node, Matcher, &DescendantBoundNodesBuilder, MaxDepth, Traversal, Bind); InsertResult.first->second.Nodes = DescendantBoundNodesBuilder.build(); @@ -269,12 +271,12 @@ } // Matches children or descendants of 'Node' with 'BaseMatcher'. - template - bool matchesRecursively(const T &Node, const UntypedBaseMatcher &BaseMatcher, + bool matchesRecursively(const ast_type_traits::DynTypedNode &Node, + const DynTypedMatcher &Matcher, BoundNodesTreeBuilder *Builder, int MaxDepth, TraversalKind Traversal, BindKind Bind) { MatchChildASTVisitor Visitor( - &BaseMatcher, this, Builder, MaxDepth, Traversal, Bind); + &Matcher, this, Builder, MaxDepth, Traversal, Bind); return Visitor.findMatch(Node); } @@ -283,36 +285,20 @@ BoundNodesTreeBuilder *Builder); // Implements ASTMatchFinder::MatchesChildOf. - virtual bool matchesChildOf(const Decl &DeclNode, - const UntypedBaseMatcher &BaseMatcher, + virtual bool matchesChildOf(const ast_type_traits::DynTypedNode &Node, + const DynTypedMatcher &Matcher, BoundNodesTreeBuilder *Builder, TraversalKind Traversal, BindKind Bind) { - return matchesRecursively(DeclNode, BaseMatcher, Builder, 1, Traversal, + return matchesRecursively(Node, Matcher, Builder, 1, Traversal, Bind); } - virtual bool matchesChildOf(const Stmt &StmtNode, - const UntypedBaseMatcher &BaseMatcher, - BoundNodesTreeBuilder *Builder, - TraversalKind Traversal, - BindKind Bind) { - return matchesRecursively(StmtNode, BaseMatcher, Builder, 1, Traversal, - Bind); - } - // Implements ASTMatchFinder::MatchesDescendantOf. - virtual bool matchesDescendantOf(const Decl &DeclNode, - const UntypedBaseMatcher &BaseMatcher, - BoundNodesTreeBuilder *Builder, - BindKind Bind) { - return memoizedMatchesRecursively(DeclNode, BaseMatcher, Builder, INT_MAX, - TK_AsIs, Bind); - } - virtual bool matchesDescendantOf(const Stmt &StmtNode, - const UntypedBaseMatcher &BaseMatcher, + virtual bool matchesDescendantOf(const ast_type_traits::DynTypedNode &Node, + const DynTypedMatcher &Matcher, BoundNodesTreeBuilder *Builder, BindKind Bind) { - return memoizedMatchesRecursively(StmtNode, BaseMatcher, Builder, INT_MAX, + return memoizedMatchesRecursively(Node, Matcher, Builder, INT_MAX, TK_AsIs, Bind); } @@ -358,12 +344,12 @@ // result callback for every node that matches. template void match(const T &node) { - for (std::vector< std::pair >::const_iterator + for (std::vector::const_iterator It = Triggers->begin(), End = Triggers->end(); It != End; ++It) { BoundNodesTreeBuilder Builder; - if (It->first->matches(node, this, &Builder)) { + if (It->first->matches(ast_type_traits::DynTypedNode::create(node), + this, &Builder)) { BoundNodesTree BoundNodes = Builder.build(); MatchVisitor Visitor(ActiveASTContext, It->second); BoundNodes.visitMatches(&Visitor); @@ -371,8 +357,7 @@ } } - std::vector< std::pair > *const Triggers; + std::vector *const Triggers; ASTContext *ActiveASTContext; // Maps a canonical type to its TypedefDecls. @@ -474,8 +459,7 @@ class MatchASTConsumer : public ASTConsumer { public: - MatchASTConsumer(std::vector< std::pair > *Triggers, + MatchASTConsumer(std::vector *Triggers, MatchFinder::ParsingDoneTestCallback *ParsingDone) : Visitor(Triggers), ParsingDone(ParsingDone) {} @@ -508,8 +492,7 @@ MatchFinder::MatchFinder() : ParsingDone(NULL) {} MatchFinder::~MatchFinder() { - for (std::vector< std::pair >::const_iterator + for (std::vector::const_iterator It = Triggers.begin(), End = Triggers.end(); It != End; ++It) { delete It->first; @@ -519,19 +502,19 @@ void MatchFinder::addMatcher(const DeclarationMatcher &NodeMatch, MatchCallback *Action) { Triggers.push_back(std::make_pair( - new internal::TypedBaseMatcher(NodeMatch), Action)); + new internal::Matcher(NodeMatch), Action)); } void MatchFinder::addMatcher(const TypeMatcher &NodeMatch, MatchCallback *Action) { Triggers.push_back(std::make_pair( - new internal::TypedBaseMatcher(NodeMatch), Action)); + new internal::Matcher(NodeMatch), Action)); } void MatchFinder::addMatcher(const StatementMatcher &NodeMatch, MatchCallback *Action) { Triggers.push_back(std::make_pair( - new internal::TypedBaseMatcher(NodeMatch), Action)); + new internal::Matcher(NodeMatch), Action)); } ASTConsumer *MatchFinder::newASTConsumer() { Index: unittests/ASTMatchers/ASTMatchersTest.cpp =================================================================== --- unittests/ASTMatchers/ASTMatchersTest.cpp +++ unittests/ASTMatchers/ASTMatchersTest.cpp @@ -1914,9 +1914,8 @@ TOOLING_COMPILE_ASSERT((llvm::is_same::value) || (llvm::is_same::value), assert_node_type_is_accessible); - internal::TypedBaseMatcher ChildMatcher(AMatcher); return Finder->matchesChildOf( - Node, ChildMatcher, Builder, + Node, AMatcher, Builder, ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses, ASTMatchFinder::BK_First); }