Hi klimek, doug.gregor, gribozavr,
To support finding bound nodes found anywhere previously in the match
expression from a matcher that was provided a BoundNodesTreeBuilder
building a sub-tree of the full bound-nodes tree, every Builder
requires:
1) A pointer back to the root
2) A way of building a bound-nodes tree of results *so far* where these
results may be later thrown away.
Item 1 is handled by adding a root pointer to each builder. To make
interface harder to mis-use Builders can only be created by a static
'root creator' function or by Grafters.
Item 2 is handled by the introduction of the "Grafter" class. It
represents 'temporary' results which may appear in a tree created from
a Builder. If a Grafter is discarded, the results are thrown away.
Grafter is intrusively ref-counted so that it can serve as an RAII
object but still be created by a Builder and ownership passed to the
caller (auto_ptr would have worked but llvm::OwningPtr will not).
This revision introduces the "equalsBoundNode()" matcher currently
implemented for QualType, Stmt, and Decl which allows it to replace
existing "equalsNode()" uses.
http://llvm-reviews.chandlerc.com/D740
Files:
include/clang/ASTMatchers/ASTMatchers.h
include/clang/ASTMatchers/ASTMatchersInternal.h
lib/ASTMatchers/ASTMatchFinder.cpp
lib/ASTMatchers/ASTMatchersInternal.cpp
unittests/ASTMatchers/ASTMatchersTest.cpp
Index: include/clang/ASTMatchers/ASTMatchers.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -52,6 +52,11 @@
#include "llvm/Support/Regex.h"
#include <iterator>
+// DEBUG
+#include "llvm/Support/raw_ostream.h"
+#include "clang/AST/ASTContext.h"
+////////
+
namespace clang {
namespace ast_matchers {
@@ -85,6 +90,8 @@
}
/// @}
+ const internal::BoundNodesMap &getNodeMap() const { return MyBoundNodes; }
+
private:
/// \brief Create BoundNodes from a pre-filled map of bindings.
BoundNodes(internal::BoundNodesMap &MyBoundNodes)
@@ -1841,6 +1848,8 @@
/// varDecl(hasType(qualType(hasCanonicalType(referenceType())))))) does.
AST_MATCHER_P(QualType, hasCanonicalType, internal::Matcher<QualType>,
InnerMatcher) {
+ if (Node.isNull())
+ return false;
return InnerMatcher.matches(Node.getCanonicalType(), Finder, Builder);
}
@@ -3329,6 +3338,12 @@
return &Node == Other;
}
+inline internal::PolymorphicMatcherWithParam1<internal::EqualsBoundNodeMatcher,
+ llvm::StringRef>
+equalsBoundNode(StringRef id) {
+ return internal::PolymorphicMatcherWithParam1<
+ internal::EqualsBoundNodeMatcher, llvm::StringRef>(id);
+}
/// @}
} // end namespace ast_matchers
Index: include/clang/ASTMatchers/ASTMatchersInternal.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchersInternal.h
+++ include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -42,6 +42,7 @@
#include "clang/AST/StmtCXX.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/Type.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/VariadicFunction.h"
#include "llvm/Support/type_traits.h"
#include <map>
@@ -60,10 +61,14 @@
namespace internal {
-class BoundNodesTreeBuilder;
/// \brief Internal version of BoundNodes. Holds all the bound nodes.
class BoundNodesMap {
public:
+ /// \brief A map from IDs to the bound nodes.
+ typedef std::map<std::string, ast_type_traits::DynTypedNode> IDToNodeMap;
+ typedef IDToNodeMap::const_iterator const_iterator;
+
+public:
/// \brief Adds \c Node to the map with key \c ID.
///
/// The node's base type should be in NodeBaseType or it will be unaccessible.
@@ -88,16 +93,13 @@
return It->second.get<T>();
}
- /// \brief Copies all ID/Node pairs to BoundNodesTreeBuilder \c Builder.
- void copyTo(BoundNodesTreeBuilder *Builder) const;
-
/// \brief Copies all ID/Node pairs to BoundNodesMap \c Other.
void copyTo(BoundNodesMap *Other) const;
-private:
- /// \brief A map from IDs to the bound nodes.
- typedef std::map<std::string, ast_type_traits::DynTypedNode> IDToNodeMap;
+ const_iterator begin() const { return NodeMap.begin(); }
+ const_iterator end() const { return NodeMap.end(); }
+private:
IDToNodeMap NodeMap;
};
@@ -130,14 +132,26 @@
BoundNodesTree(const BoundNodesMap& Bindings,
const std::vector<BoundNodesTree> RecursiveBindings);
- /// \brief Adds all bound nodes to \c Builder.
- void copyTo(BoundNodesTreeBuilder* Builder) const;
-
/// \brief Visits all matches that this BoundNodesTree represents.
///
/// The ownership of 'ResultVisitor' remains at the caller.
void visitMatches(Visitor* ResultVisitor);
+ /// \brief Accessor for the bound nodes for this node in the bound-nodes
+ /// tree.
+ const BoundNodesMap &getBoundNodes() const { return Bindings; }
+
+ /// \brief Iterator to the beginning of the vector of children of this
+ /// node in the bound-nodes tree.
+ std::vector<BoundNodesTree>::const_iterator branches_begin() const {
+ return RecursiveBindings.begin();
+ }
+ /// \brief Iterator to the end of the vector of children of this node in the
+ /// bound-nodes tree.
+ std::vector<BoundNodesTree>::const_iterator branches_end() const {
+ return RecursiveBindings.end();
+ }
+
private:
void visitMatchesRecursively(
Visitor* ResultVistior,
@@ -157,7 +171,55 @@
/// nodes from the Id matcher.
class BoundNodesTreeBuilder {
public:
- BoundNodesTreeBuilder();
+ class Grafter : public llvm::RefCountedBase<Grafter> {
+ public:
+ enum Kind {
+ GK_Branch,
+ GK_Merge
+ };
+ ~Grafter();
+
+ BoundNodesTreeBuilder *getBuilder();
+ const BoundNodesTreeBuilder *getBuilder() const;
+
+ Kind getKind() const { return GraftKind; }
+
+ void apply();
+
+ friend class BoundNodesTreeBuilder;
+ private:
+ Grafter(BoundNodesTreeBuilder *Parent,
+ Kind GraftKind);
+
+ BoundNodesTreeBuilder *Parent;
+ llvm::OwningPtr<BoundNodesTreeBuilder> SubTreeBuilder;
+ Kind GraftKind;
+ };
+
+ typedef llvm::IntrusiveRefCntPtr<Grafter> GraftPtr;
+
+public:
+ /// \brief Create a new root BoundNodesTreeBuilder.
+ static BoundNodesTreeBuilder *createRootBuilder() {
+ return new BoundNodesTreeBuilder(0);
+ }
+
+ /// \brief Creates a Grafter object to represent a BoundNodesTreeBuilder
+ /// that, upon graft application, will be added as a branch of the tree this
+ /// builder is building.
+ GraftPtr makeBranch();
+
+ /// \brief Creates a Grafter object that represents a BoundNodesTreeBuilder
+ /// whose tree will be merged into the tree this builder is building upon
+ /// graft application.
+ GraftPtr makeCheckpoint();
+
+ /// \brief Returns a reference to the builder for the root of the tree being
+ /// built. 'this' may only be a subtree of the root depending on which
+ /// matchers have been used in the match expression.
+ const BoundNodesTreeBuilder *getRoot() const {
+ return (Root == 0 ? this : Root);
+ }
/// \brief Add a binding from an id to a node.
template <typename T>
@@ -168,19 +230,37 @@
Bindings.addNode(Id, Node);
}
- /// \brief Adds a branch in the tree.
- void addMatch(const BoundNodesTree& Bindings);
+ /// \brief Add the bound nodes and branches of a bound-nodes tree to the
+ /// bound nodes and branches of the tree this builder is building.
+ void mergeFrom(const BoundNodesTree& Other);
/// \brief Returns a BoundNodes object containing all current bindings.
BoundNodesTree build() const;
private:
BoundNodesTreeBuilder(const BoundNodesTreeBuilder &) LLVM_DELETED_FUNCTION;
void operator=(const BoundNodesTreeBuilder &) LLVM_DELETED_FUNCTION;
- BoundNodesMap Bindings;
+ BoundNodesTreeBuilder(const BoundNodesTreeBuilder *Root);
+
+ /// \brief Indicates to the builder the graft can be discarded. Called by
+ /// Grafter instances when they destruct. Another graft may be created
+ /// immediately upon return.
+ void discardGraft();
+ /// \brief Indicates to the builder that the current state of the graft can
+ /// be applied to the tree being built. Called by Grafter instances. Calling
+ /// this function disassociates the current grafter from the Builder allowing
+ /// for another graft to be created immediately upon return.
+ void applyGraft();
+
+ /// \brief Whether or not this builder has an active graft.
+ bool hasGraft() const { return Graft != 0; }
+
+ BoundNodesMap Bindings;
std::vector<BoundNodesTree> RecursiveBindings;
+ const BoundNodesTreeBuilder *Root;
+ const Grafter *Graft;
};
class ASTMatchFinder;
@@ -914,15 +994,16 @@
virtual bool matches(const T &Node, ASTMatchFinder *Finder,
BoundNodesTreeBuilder *Builder) const {
- BoundNodesTreeBuilder Builder1;
- bool Matched1 = InnerMatcher1.matches(Node, Finder, &Builder1);
+ BoundNodesTreeBuilder::GraftPtr Graft = Builder->makeBranch();
+ bool Matched1 = InnerMatcher1.matches(Node, Finder, Graft->getBuilder());
if (Matched1)
- Builder->addMatch(Builder1.build());
+ Graft->apply();
- BoundNodesTreeBuilder Builder2;
- bool Matched2 = InnerMatcher2.matches(Node, Finder, &Builder2);
+ Graft.reset();
+ Graft = Builder->makeBranch();
+ bool Matched2 = InnerMatcher2.matches(Node, Finder, Graft->getBuilder());
if (Matched2)
- Builder->addMatch(Builder2.build());
+ Graft->apply();
return Matched1 || Matched2;
}
@@ -1238,6 +1319,80 @@
return T(makeAllOfComposite<InnerT>(InnerMatchers));
}
+/// \brief A BoundNodesTree Visitor for finding a bound node with a given id.
+class BoundNodeFinder : public BoundNodesTree::Visitor {
+public:
+ BoundNodeFinder(StringRef Id) : Id(Id), Found(false) {}
+
+ virtual void visitMatch(const BoundNodes& Nodes) LLVM_OVERRIDE;
+
+ template <typename T>
+ const T *getResult() const {
+ return Result.get<T>();
+ }
+
+private:
+ StringRef Id;
+ bool Found;
+ ast_type_traits::DynTypedNode Result;
+};
+
+/// \brief Matches nodes of type \c T that \em equal another node bound with the
+/// id provided to the constructor. Bound nodes that are not of type \c T will
+/// never match.
+///
+/// Currently implemented for T in {QualType, Decl, Stmt}. For Decl and Stmt,
+/// equality is defined as pointer equality. For QualType, equality is defined
+/// by QualType::operator==().
+template <typename T, typename ArgT>
+class EqualsBoundNodeMatcher : public MatcherInterface<T> {
+ TOOLING_COMPILE_ASSERT((llvm::is_same<ArgT,
+ llvm::StringRef>::value &&
+ (llvm::is_same<T, Decl>::value ||
+ llvm::is_same<T, Stmt>::value ||
+ llvm::is_same<T, QualType>::value)),
+ sameAs_matcher_requires_StringRef_argument);
+
+public:
+ explicit EqualsBoundNodeMatcher(llvm::StringRef Id) : Id(Id) {}
+
+ virtual bool matches(const T &Node,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const LLVM_OVERRIDE {
+
+ // Build tree of matches from the root builder so we can see bound nodes
+ // found above our level of the tree.
+ BoundNodesTree BoundNodes = Builder->getRoot()->build();
+ BoundNodeFinder Visitor(Id);
+ BoundNodes.visitMatches(&Visitor);
+
+ const T *BoundNode = Visitor.getResult<T>();
+ if (!BoundNode) {
+ return false;
+ }
+ return matchesSpecialized(Node, *BoundNode, Finder, Builder);
+ }
+
+ // For QualType use QualType::operator==().
+ bool matchesSpecialized(const QualType &Node,
+ const QualType &BoundNode,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return Node == BoundNode;
+ }
+
+ // For everything else, use pointer identity as the test.
+ template <typename U>
+ bool matchesSpecialized(const U &Node, const U &BoundNode,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return &Node == &BoundNode;
+ }
+
+private:
+ std::string Id;
+};
+
} // end namespace internal
} // end namespace ast_matchers
} // end namespace clang
Index: lib/ASTMatchers/ASTMatchFinder.cpp
===================================================================
--- lib/ASTMatchers/ASTMatchFinder.cpp
+++ lib/ASTMatchers/ASTMatchFinder.cpp
@@ -226,12 +226,12 @@
return false; // Abort as soon as a match is found.
}
} else {
- BoundNodesTreeBuilder RecursiveBuilder;
+ BoundNodesTreeBuilder::GraftPtr Graft = Builder->makeBranch();
if (Matcher->matches(ast_type_traits::DynTypedNode::create(Node),
- Finder, &RecursiveBuilder)) {
+ Finder, Graft->getBuilder())) {
// After the first match the matcher succeeds.
Matches = true;
- Builder->addMatch(RecursiveBuilder.build());
+ Graft->apply();
}
}
return true;
@@ -342,14 +342,15 @@
std::pair<MemoizationMap::iterator, bool> InsertResult
= ResultCache.insert(std::make_pair(input, MemoizedMatchResult()));
if (InsertResult.second) {
- BoundNodesTreeBuilder DescendantBoundNodesBuilder;
+ BoundNodesTreeBuilder::GraftPtr Graft = Builder->makeCheckpoint();
InsertResult.first->second.ResultOfMatch =
- matchesRecursively(Node, Matcher, &DescendantBoundNodesBuilder,
+ matchesRecursively(Node, Matcher, Graft->getBuilder(),
MaxDepth, Traversal, Bind);
- InsertResult.first->second.Nodes =
- DescendantBoundNodesBuilder.build();
+ InsertResult.first->second.Nodes = Graft->getBuilder()->build();
+ Graft->apply();
+ } else {
+ Builder->mergeFrom(InsertResult.first->second.Nodes);
}
- InsertResult.first->second.Nodes.copyTo(Builder);
return InsertResult.first->second.ResultOfMatch;
}
@@ -400,9 +401,10 @@
MatchCallback*> >::const_iterator
I = MatcherCallbackPairs->begin(), E = MatcherCallbackPairs->end();
I != E; ++I) {
- BoundNodesTreeBuilder Builder;
- if (I->first->matches(Node, this, &Builder)) {
- BoundNodesTree BoundNodes = Builder.build();
+ llvm::OwningPtr<BoundNodesTreeBuilder> Builder(BoundNodesTreeBuilder::createRootBuilder());
+
+ if (I->first->matches(Node, this, Builder.get())) {
+ BoundNodesTree BoundNodes = Builder->build();
MatchVisitor Visitor(ActiveASTContext, I->second);
BoundNodes.visitMatches(&Visitor);
}
@@ -453,25 +455,25 @@
const UntypedMatchInput input(Matcher.getID(), Node.getMemoizationData());
MemoizationMap::iterator I = ResultCache.find(input);
if (I == ResultCache.end()) {
- BoundNodesTreeBuilder AncestorBoundNodesBuilder;
+ BoundNodesTreeBuilder::GraftPtr Graft = Builder->makeCheckpoint();
bool Matches = false;
if (Parents.size() == 1) {
// Only one parent - do recursive memoization.
const ast_type_traits::DynTypedNode Parent = Parents[0];
- if (Matcher.matches(Parent, this, &AncestorBoundNodesBuilder)) {
+ if (Matcher.matches(Parent, this, Graft->getBuilder())) {
Matches = true;
} else if (MatchMode != ASTMatchFinder::AMM_ParentOnly) {
Matches = memoizedMatchesAncestorOfRecursively(
- Parent, Matcher, &AncestorBoundNodesBuilder, MatchMode);
+ Parent, Matcher, Graft->getBuilder(), MatchMode);
}
} else {
// Multiple parents - BFS over the rest of the nodes.
llvm::DenseSet<const void *> Visited;
std::deque<ast_type_traits::DynTypedNode> Queue(Parents.begin(),
Parents.end());
while (!Queue.empty()) {
if (Matcher.matches(Queue.front(), this,
- &AncestorBoundNodesBuilder)) {
+ Graft->getBuilder())) {
Matches = true;
break;
}
@@ -494,10 +496,12 @@
I = ResultCache.insert(std::make_pair(input, MemoizedMatchResult()))
.first;
- I->second.Nodes = AncestorBoundNodesBuilder.build();
+ I->second.Nodes = Graft->getBuilder()->build();
+ Graft->apply();
I->second.ResultOfMatch = Matches;
+ } else {
+ Builder->mergeFrom(I->second.Nodes);
}
- I->second.Nodes.copyTo(Builder);
return I->second.ResultOfMatch;
}
Index: lib/ASTMatchers/ASTMatchersInternal.cpp
===================================================================
--- lib/ASTMatchers/ASTMatchersInternal.cpp
+++ lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -14,18 +14,25 @@
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchersInternal.h"
+namespace {
+using namespace clang::ast_matchers::internal;
+
+/// \brief Helper function to copy the bound nodes and list of bounds-node tree
+/// branches from a BoundNodesTree and add them to provided containers.
+void mergeTree(BoundNodesMap &DestBindings,
+ std::vector<BoundNodesTree> &DestBranches,
+ const BoundNodesTree &Src) {
+ Src.getBoundNodes().copyTo(&DestBindings);
+ std::copy(Src.branches_begin(), Src.branches_end(),
+ std::back_inserter(DestBranches));
+}
+
+}
+
namespace clang {
namespace ast_matchers {
namespace internal {
-void BoundNodesMap::copyTo(BoundNodesTreeBuilder *Builder) const {
- for (IDToNodeMap::const_iterator It = NodeMap.begin();
- It != NodeMap.end();
- ++It) {
- Builder->setBinding(It->first, It->second);
- }
-}
-
void BoundNodesMap::copyTo(BoundNodesMap *Other) const {
for (IDToNodeMap::const_iterator I = NodeMap.begin(),
E = NodeMap.end();
@@ -42,16 +49,6 @@
: Bindings(Bindings),
RecursiveBindings(RecursiveBindings) {}
-void BoundNodesTree::copyTo(BoundNodesTreeBuilder* Builder) const {
- Bindings.copyTo(Builder);
- for (std::vector<BoundNodesTree>::const_iterator
- I = RecursiveBindings.begin(),
- E = RecursiveBindings.end();
- I != E; ++I) {
- Builder->addMatch(*I);
- }
-}
-
void BoundNodesTree::visitMatches(Visitor* ResultVisitor) {
BoundNodesMap AggregatedBindings;
visitMatchesRecursively(ResultVisitor, AggregatedBindings);
@@ -72,14 +69,116 @@
}
}
-BoundNodesTreeBuilder::BoundNodesTreeBuilder() {}
+BoundNodesTreeBuilder::Grafter::Grafter(BoundNodesTreeBuilder *Parent,
+ Kind GraftKind)
+ : Parent(Parent),
+ SubTreeBuilder(new BoundNodesTreeBuilder(Parent->getRoot())),
+ GraftKind(GraftKind) {}
+
+BoundNodesTreeBuilder::Grafter::~Grafter() {
+ Parent->discardGraft();
+ SubTreeBuilder.reset();
+}
+
+BoundNodesTreeBuilder *BoundNodesTreeBuilder::Grafter::getBuilder() {
+ assert(SubTreeBuilder && "Graft Builder is NULL");
+ return SubTreeBuilder.get();
+}
+
+const BoundNodesTreeBuilder *
+BoundNodesTreeBuilder::Grafter::getBuilder() const {
+ return const_cast<Grafter*>(this)->getBuilder();
+}
+
+void BoundNodesTreeBuilder::Grafter::apply() {
+ Parent->applyGraft();
+ SubTreeBuilder.reset();
+}
+
+BoundNodesTreeBuilder::BoundNodesTreeBuilder(const BoundNodesTreeBuilder *Root)
+ : Root(Root), Graft(0) {}
+
+BoundNodesTreeBuilder::GraftPtr BoundNodesTreeBuilder::makeBranch() {
+ assert(Graft == 0 && "Graft already exists");
+ Grafter *RawGraft = new Grafter(this, Grafter::GK_Branch);
+ Graft = RawGraft;
+ return RawGraft;
+}
+
+BoundNodesTreeBuilder::GraftPtr BoundNodesTreeBuilder::makeCheckpoint() {
+ assert(Graft == 0 && "Graft already exists");
+ Grafter *RawGraft = new Grafter(this, Grafter::GK_Merge);
+ Graft = RawGraft;
+ return RawGraft;
+}
+
+void BoundNodesTreeBuilder::applyGraft() {
+ if (!Graft) {
+ return;
+ }
+
+ // applyGraft() is a permanent operation to the tree used as the tree is
+ // being built. Recursive application of grafts would imply permanently
+ // applying possibly temporary results. See build() for a recursive
+ // applicaiton of grafts.
+ assert(!Graft->getBuilder()->hasGraft() &&
+ "Can't apply a graft that has also has a graft");
+
+ const BoundNodesTree &SubTree = Graft->getBuilder()->build();
+
+ if (Graft->getKind() == Grafter::GK_Branch) {
+ RecursiveBindings.push_back(SubTree);
+ } else if (Graft->getKind() == Grafter::GK_Merge) {
+ mergeTree(Bindings, RecursiveBindings, SubTree);
+ }
+
+ discardGraft();
+}
+
+void BoundNodesTreeBuilder::discardGraft()
+{
+ Graft = 0;
+}
-void BoundNodesTreeBuilder::addMatch(const BoundNodesTree& Bindings) {
- RecursiveBindings.push_back(Bindings);
+void BoundNodesTreeBuilder::mergeFrom(const BoundNodesTree& Other)
+{
+ mergeTree(Bindings, RecursiveBindings, Other);
}
BoundNodesTree BoundNodesTreeBuilder::build() const {
- return BoundNodesTree(Bindings, RecursiveBindings);
+ if (!hasGraft()) {
+ return BoundNodesTree(Bindings, RecursiveBindings);
+ }
+
+ // Recursively apply grafts to copies of this builder's internal data.
+ BoundNodesTree TempSubTree = Graft->getBuilder()->build();
+ switch (Graft->getKind()) {
+ case Grafter::GK_Branch: {
+ std::vector<BoundNodesTree> TempBranches(RecursiveBindings);
+ TempBranches.push_back(TempSubTree);
+ return BoundNodesTree(Bindings, TempBranches);
+ }
+ case Grafter::GK_Merge: {
+ BoundNodesMap TempBindings(Bindings);
+ std::vector<BoundNodesTree> TempBranches(RecursiveBindings);
+ mergeTree(TempBindings, TempBranches, TempSubTree);
+ return BoundNodesTree(TempBindings, TempBranches);
+ }
+ }
+}
+
+void BoundNodeFinder::visitMatch(const BoundNodes& Nodes) {
+ if (Found) return;
+
+ for (BoundNodesMap::const_iterator I = Nodes.getNodeMap().begin(),
+ E = Nodes.getNodeMap().end();
+ I != E; ++I) {
+ if (Id.equals(I->first)) {
+ Result = I->second;
+ Found = true;
+ break;
+ }
+ }
}
} // end namespace internal
Index: unittests/ASTMatchers/ASTMatchersTest.cpp
===================================================================
--- unittests/ASTMatchers/ASTMatchersTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTest.cpp
@@ -3900,5 +3900,147 @@
EXPECT_TRUE(VerifyCallback.Called);
}
+TEST(EqualsBoundNodeMatcher, QualType) {
+ EXPECT_TRUE(matches("int i = 1;",
+ varDecl(hasType(qualType().bind("type")),
+ hasInitializer(ignoringParenImpCasts(hasType(qualType(equalsBoundNode("type"))))))));
+ EXPECT_TRUE(notMatches("int i = 1.f;",
+ varDecl(hasType(qualType().bind("type")),
+ hasInitializer(ignoringParenImpCasts(hasType(qualType(equalsBoundNode("type"))))))));
+}
+
+TEST(EqualsBoundNodeMatcher, NonMatchingTypes) {
+ EXPECT_TRUE(notMatches("int i = 1;",
+ varDecl(namedDecl(hasName("i")).bind("name"),
+ hasInitializer(ignoringParenImpCasts(hasType(qualType(equalsBoundNode("type"))))))));
+}
+
+TEST(EqualsBoundNodeMatcher, Stmt) {
+ // A re-implementation of the test IsEqualTo.MatchesNodesByIdentity which uses
+ // equalsNode().
+ EXPECT_TRUE(matches("void f() { if(true) {} }",
+ stmt(allOf(ifStmt().bind("if"),
+ hasParent(stmt(has(stmt(equalsBoundNode("if"))))
+ )))));
+
+ EXPECT_TRUE(notMatches("void f() { if(true) { if (true) {} } }",
+ stmt(allOf(ifStmt().bind("if"),
+ has(stmt(equalsBoundNode("if"))
+ )))));
+}
+
+TEST(EqualsBoundNodeMatcher, Decl) {
+ // A re-implementation of the test IsEqualTo.MatchesNodesByIdentity which uses
+ // equalsNode().
+ EXPECT_TRUE(matches("class X { class Y {}; };",
+ decl(allOf(recordDecl(hasName("::X::Y")).bind("record"),
+ hasParent(decl(has(decl(equalsBoundNode("record")))))
+ ))));
+
+ EXPECT_TRUE(notMatches("class X { class Y {}; };",
+ decl(allOf(recordDecl(hasName("::X")).bind("record"),
+ has(decl(equalsBoundNode("record")))
+ ))));
+}
+
+TEST(SameAsMatcher, UsingForEachDescendant) {
+ const std::string Fragment = "\
+ int f() { \
+ if (1) { \
+ int i = 9; \
+ } \
+ int j = 10; \
+ return j; \
+ } \
+ float g() { \
+ int k = 9.0; \
+ return k; \
+ }";
+
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ Fragment,
+ // Look for variable declarations within functions whose type is the same
+ // as the function return type.
+ functionDecl(returns(qualType().bind("type")),
+ forEachDescendant(
+ varDecl(
+ hasType(
+ qualType(
+ equalsBoundNode("type")))).bind("decl"))),
+ // Only i and j should match, not k.
+ new VerifyIdIsBoundTo<VarDecl>("decl", 2)));
+
+}
+
+TEST(EqualsBoundNodeMatcher, MatchesFirstBoundNode) {
+ // This extremely contrived matcher goes looking for VarDecls within IfStmts
+ // within FunctionDecls and binds the type of every found VarDecl with the id
+ // 'declType'. Then it looks for a call expression that's an immediate child
+ // of the FunctionDecl (technically within the function's CompoundStmt) and
+ // matches only if the return type of that function is the same as the
+ // *first* VarDecl found previously.
+ DeclarationMatcher matcher =
+ functionDecl(
+ forEachDescendant(
+ ifStmt(
+ forEachDescendant(
+ varDecl(hasType(qualType().bind("declType")))
+ )
+ )
+ ),
+ has(compoundStmt(has(
+ callExpr(
+ callee(
+ functionDecl(
+ returns(qualType(equalsBoundNode("declType")))
+ )
+ )
+ )
+ )))
+ );
+
+ // This fragment will match because the first varDecl found is 'int a' and
+ // the type matches the return type of iCall().
+ const std::string MatchingFragment = "\
+ int iCall(); \
+ int f() { \
+ iCall(); \
+ if (1) { \
+ { \
+ int a; \
+ } \
+ } \
+ while (1) { \
+ if (1) { \
+ float c; \
+ } \
+ } \
+ return 0; \
+ } \
+ ";
+ // This fragment will NOT match because although a var decl is found with
+ // type float, the *first* one found is that of 'int a' and there's no call
+ // to a function whose return type is 'int'.
+ const std::string NegativeFragment = "\
+ float fCall(); \
+ int f() { \
+ fCall(); \
+ if (1) { \
+ { \
+ int a; \
+ } \
+ } \
+ while (1) { \
+ if (1) { \
+ float c; \
+ } \
+ } \
+ return 0; \
+ } \
+ ";
+ EXPECT_TRUE(matches(MatchingFragment, matcher));
+ EXPECT_TRUE(notMatches(NegativeFragment, matcher));
+}
+
} // end namespace ast_matchers
} // end namespace clang
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits