Hi klimek, doug.gregor, gribozavr,
Sub-expression references implemented without Grafter
Removed Grafter. Builders only maintain parent links. When doing a
search through the tree of bound nodes BoundNodesTree::visitMatches()
isn't used. Instead, a search is conducted upwards from the current
builder-under-construction toward the root. An iterator class
encapsulates the logic. The search order implemented by the iterator was
not chosen for any particular reason as the search order isn't important
for existing matchers.
Also, this new implementation searches beyond the first node that
matches by name and type and keeps looking until a node matches on the
equalsBoundNode() identity condition if possible.
Disabled the test that specifically ensured the aforementioned "keep
searching" algorithm wasn't employed.
Commenting of code and updates to equalsBoundNode matcher tests will
come after high level review of search logic is reviewed.
http://llvm-reviews.chandlerc.com/D788
Files:
include/clang/ASTMatchers/ASTMatchers.h
include/clang/ASTMatchers/ASTMatchersInternal.h
lib/ASTMatchers/ASTMatchFinder.cpp
lib/ASTMatchers/ASTMatchersInternal.cpp
unittests/ASTMatchers/ASTMatchersTest.cpp
unittests/ASTMatchers/BoundNodesIteratorTest.cpp
unittests/ASTMatchers/CMakeLists.txt
Index: include/clang/ASTMatchers/ASTMatchers.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -3346,6 +3346,13 @@
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
@@ -60,10 +60,13 @@
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;
+
/// \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,19 +91,23 @@
return It->second.get<T>();
}
- /// \brief Copies all ID/Node pairs to BoundNodesTreeBuilder \c Builder.
- void copyTo(BoundNodesTreeBuilder *Builder) const;
+ /// \brief Return an iterator to the first bound node.
+ const_iterator begin() const { return NodeMap.begin(); }
+
+ /// \brief Return an iterator to the end of bound nodes.
+ const_iterator end() const { return NodeMap.end(); }
/// \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;
IDToNodeMap NodeMap;
};
+class BoundNodesTree;
+typedef std::vector<BoundNodesTree>::const_iterator const_branch_iterator;
+
/// \brief A tree of bound nodes in match results.
///
/// If a match can contain multiple matches on the same node with different
@@ -130,18 +137,30 @@
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.
+ const_branch_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.
+ const_branch_iterator branches_end() const {
+ return RecursiveBindings.end();
+ }
+
private:
- void visitMatchesRecursively(
- Visitor* ResultVistior,
- const BoundNodesMap& AggregatedBindings);
+ void visitMatchesRecursively(Visitor *ResultVistior,
+ const BoundNodesMap &AggregatedBindings);
// FIXME: Find out whether we want to use different data structures here -
// first benchmarks indicate that it doesn't matter though.
@@ -151,13 +170,84 @@
std::vector<BoundNodesTree> RecursiveBindings;
};
+class BoundNodesTreeBuilder;
+
+struct BoundNodeTest {
+ virtual ~BoundNodeTest() {}
+ virtual bool operator()(BoundNodesMap::const_iterator) const = 0;
+};
+
+class BoundNodesTreeIteratorBase {
+public:
+ BoundNodesTreeIteratorBase(const BoundNodesTreeBuilder *Start,
+ const BoundNodeTest &Predicate)
+ : Predicate(Predicate), CurrBuilder(Start) {}
+
+ void operator++();
+ bool operator==(const BoundNodesTreeIteratorBase &Other) const;
+ bool operator!=(const BoundNodesTreeIteratorBase &Other) const {
+ return !(*this == Other);
+ }
+
+ template <typename T>
+ const T *get() const {
+ if (!CurrBuilder)
+ return 0;
+ return CurrBoundNode->second.get<T>();
+ }
+
+protected:
+ void init();
+
+private:
+ void advance();
+ bool advanceToNextBranch();
+
+ const BoundNodeTest& Predicate;
+
+ // Internal state for controlling where the iterator looks next for bound
+ // nodes.
+ std::vector<std::pair<const BoundNodesTree *, const_branch_iterator> >
+ ParentStack;
+ const BoundNodesTreeBuilder *CurrBuilder;
+ BoundNodesMap::const_iterator CurrBoundNode;
+};
+
+template <typename T>
+class BoundNodesTreeIterator : public BoundNodesTreeIteratorBase {
+public:
+ struct BoundNodeIdAndTypeTest : BoundNodeTest {
+ BoundNodeIdAndTypeTest(StringRef Id) : Id(Id) {}
+
+ virtual bool operator()(BoundNodesMap::const_iterator I) const
+ LLVM_OVERRIDE {
+ return Id.equals(I->first) && I->second.get<T>() != 0;
+ }
+
+ private:
+ StringRef Id;
+ };
+
+ BoundNodesTreeIterator(const BoundNodesTreeBuilder *Start, StringRef Id)
+ : BoundNodesTreeIteratorBase(Start, Predicate), Predicate(Id) {
+ init();
+ }
+
+ const T *operator*() const {
+ return get<T>();
+ };
+
+private:
+ BoundNodeIdAndTypeTest Predicate;
+};
+
/// \brief Creates BoundNodesTree objects.
///
/// The tree builder is used during the matching process to insert the bound
/// nodes from the Id matcher.
class BoundNodesTreeBuilder {
public:
- BoundNodesTreeBuilder();
+ BoundNodesTreeBuilder(BoundNodesTreeBuilder *Parent);
/// \brief Add a binding from an id to a node.
template <typename T>
@@ -168,19 +258,57 @@
Bindings.addNode(Id, Node);
}
- /// \brief Adds a branch in the tree.
- void addMatch(const BoundNodesTree& Bindings);
+ /// \brief Adds the tree this builder represents as a branch of the parent
+ /// builder.
+ void addAsBranch();
+
+ /// \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;
+ /// \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.
+ const_branch_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.
+ const_branch_iterator branches_end() const {
+ return RecursiveBindings.end();
+ }
+
+ /// \brief Accessor for parent of this builder.
+ /// \returns NULL if this builder is the root builder.
+ const BoundNodesTreeBuilder *getParent() const { return Parent; }
+
+ template <typename T>
+ BoundNodesTreeIterator<T> bound_nodes_begin(StringRef Id) const {
+ return BoundNodesTreeIterator<T>(this, Id);
+ }
+ template <typename T>
+ BoundNodesTreeIterator<T> bound_nodes_end() const {
+ return BoundNodesTreeIterator<T>(0, "");
+ }
+
private:
+
+ void addBranch(const BoundNodesTreeBuilder &SubBuilder);
+
BoundNodesTreeBuilder(const BoundNodesTreeBuilder &) LLVM_DELETED_FUNCTION;
void operator=(const BoundNodesTreeBuilder &) LLVM_DELETED_FUNCTION;
BoundNodesMap Bindings;
-
std::vector<BoundNodesTree> RecursiveBindings;
+
+ BoundNodesTreeBuilder *Parent;
};
class ASTMatchFinder;
@@ -914,15 +1042,15 @@
virtual bool matches(const T &Node, ASTMatchFinder *Finder,
BoundNodesTreeBuilder *Builder) const {
- BoundNodesTreeBuilder Builder1;
+ BoundNodesTreeBuilder Builder1(Builder);
bool Matched1 = InnerMatcher1.matches(Node, Finder, &Builder1);
if (Matched1)
- Builder->addMatch(Builder1.build());
+ Builder1.addAsBranch();
- BoundNodesTreeBuilder Builder2;
+ BoundNodesTreeBuilder Builder2(Builder);
bool Matched2 = InnerMatcher2.matches(Node, Finder, &Builder2);
if (Matched2)
- Builder->addMatch(Builder2.build());
+ Builder2.addAsBranch();
return Matched1 || Matched2;
}
@@ -1238,6 +1366,59 @@
return T(makeAllOfComposite<InnerT>(InnerMatchers));
}
+/// \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 {
+
+ for (BoundNodesTreeIterator<T> I = Builder->bound_nodes_begin<T>(Id),
+ E = Builder->bound_nodes_end<T>();
+ I != E; ++I) {
+ if (matchesSpecialized(Node, **I, Finder, Builder)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // 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 RecursiveBuilder(Builder);
if (Matcher->matches(ast_type_traits::DynTypedNode::create(Node),
Finder, &RecursiveBuilder)) {
// After the first match the matcher succeeds.
Matches = true;
- Builder->addMatch(RecursiveBuilder.build());
+ RecursiveBuilder.addAsBranch();
}
}
return true;
@@ -342,14 +342,13 @@
std::pair<MemoizationMap::iterator, bool> InsertResult
= ResultCache.insert(std::make_pair(input, MemoizedMatchResult()));
if (InsertResult.second) {
- BoundNodesTreeBuilder DescendantBoundNodesBuilder;
+ BoundNodesTreeBuilder DescendantBoundNodesBuilder(Builder);
InsertResult.first->second.ResultOfMatch =
matchesRecursively(Node, Matcher, &DescendantBoundNodesBuilder,
MaxDepth, Traversal, Bind);
- InsertResult.first->second.Nodes =
- DescendantBoundNodesBuilder.build();
+ InsertResult.first->second.Nodes = DescendantBoundNodesBuilder.build();
}
- InsertResult.first->second.Nodes.copyTo(Builder);
+ Builder->mergeFrom(InsertResult.first->second.Nodes);
return InsertResult.first->second.ResultOfMatch;
}
@@ -400,7 +399,8 @@
MatchCallback*> >::const_iterator
I = MatcherCallbackPairs->begin(), E = MatcherCallbackPairs->end();
I != E; ++I) {
- BoundNodesTreeBuilder Builder;
+ // Root BoundNodesTreeBuilder
+ BoundNodesTreeBuilder Builder(NULL);
if (I->first->matches(Node, this, &Builder)) {
BoundNodesTree BoundNodes = Builder.build();
MatchVisitor Visitor(ActiveASTContext, I->second);
@@ -453,7 +453,7 @@
const UntypedMatchInput input(Matcher.getID(), Node.getMemoizationData());
MemoizationMap::iterator I = ResultCache.find(input);
if (I == ResultCache.end()) {
- BoundNodesTreeBuilder AncestorBoundNodesBuilder;
+ BoundNodesTreeBuilder AncestorBoundNodesBuilder(Builder);
bool Matches = false;
if (Parents.size() == 1) {
// Only one parent - do recursive memoization.
@@ -497,7 +497,7 @@
I->second.Nodes = AncestorBoundNodesBuilder.build();
I->second.ResultOfMatch = Matches;
}
- I->second.Nodes.copyTo(Builder);
+ Builder->mergeFrom(I->second.Nodes);
return I->second.ResultOfMatch;
}
Index: lib/ASTMatchers/ASTMatchersInternal.cpp
===================================================================
--- lib/ASTMatchers/ASTMatchersInternal.cpp
+++ lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -18,14 +18,6 @@
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 +34,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,10 +54,113 @@
}
}
-BoundNodesTreeBuilder::BoundNodesTreeBuilder() {}
+void BoundNodesTreeIteratorBase::init() {
+ if (!CurrBuilder)
+ return;
+
+ ParentStack.push_back(
+ std::make_pair<const BoundNodesTree *>(0, CurrBuilder->branches_begin()));
+ CurrBoundNode = CurrBuilder->getBoundNodes().begin();
+ advance();
+}
+
+void BoundNodesTreeIteratorBase::operator++() {
+ ++CurrBoundNode;
+ advance();
+}
+
+bool BoundNodesTreeIteratorBase::operator==(
+ const BoundNodesTreeIteratorBase &Other) const {
+ if (CurrBuilder && Other.CurrBuilder)
+ return ParentStack.back() == Other.ParentStack.back() &&
+ CurrBoundNode == Other.CurrBoundNode;
+ else
+ return CurrBuilder == Other.CurrBuilder;
+}
+
+/// Advances the iterator to the next bound node whose id and dynamic type match
+/// what's being looked for.
+void BoundNodesTreeIteratorBase::advance() {
+ if (!CurrBuilder)
+ return;
+ assert(!ParentStack.empty());
+
+ for (;;) {
+ BoundNodesMap::const_iterator NodeEnd;
+ if (ParentStack.back().first)
+ NodeEnd = ParentStack.back().first->getBoundNodes().end();
+ else
+ NodeEnd = CurrBuilder->getBoundNodes().end();
+
+ if (CurrBoundNode != NodeEnd) {
+ // test and/or advance
+ if (Predicate(CurrBoundNode))
+ return;
+ ++CurrBoundNode;
+ continue;
+ }
+
+ // All bound nodes at this node have been visited. Advance to the next
+ // sub-tree to visit.
+ if (!advanceToNextBranch()) {
+ // If there are no more branches left to visit, the subtree rooted at
+ // CurrBuilder has been completely visited. Move up to its parent.
+ CurrBuilder = CurrBuilder->getParent();
+
+ // When we've reached the top of the tree, we're done iterating.
+ if (!CurrBuilder)
+ break;
+ ParentStack.push_back(
+ std::make_pair<BoundNodesTree *>(0, CurrBuilder->branches_begin()));
+ CurrBoundNode = CurrBuilder->getBoundNodes().begin();
+ continue;
+ }
+ }
+}
+
+bool BoundNodesTreeIteratorBase::advanceToNextBranch() {
+ for (;;) {
+ const_branch_iterator BranchEnd;
+ if (ParentStack.back().first)
+ BranchEnd = ParentStack.back().first->branches_end();
+ else
+ BranchEnd = CurrBuilder->branches_end();
+
+ if (ParentStack.back().second != BranchEnd) {
+ // This is an unvisited branch. Push it on the stack and prepare to
+ // visit it.
+ const BoundNodesTree &SubTree = *ParentStack.back().second;
+ ParentStack.push_back(std::make_pair(&SubTree, SubTree.branches_begin()));
+ CurrBoundNode = SubTree.getBoundNodes().begin();
+ return true;
+ }
+
+ ParentStack.pop_back();
+ if (ParentStack.empty())
+ return false;
+ ++ParentStack.back().second;
+ }
+ llvm_unreachable("BoundNodesTreeIterator::advanceToNextBranch()");
+}
+
+BoundNodesTreeBuilder::BoundNodesTreeBuilder(BoundNodesTreeBuilder *Parent)
+ : Parent(Parent) {}
+
+void BoundNodesTreeBuilder::addAsBranch() {
+ assert(Parent != 0 && "Cannot add branch. No parent set");
+ Parent->addBranch(*this);
+ Parent = 0;
+}
+
+void BoundNodesTreeBuilder::mergeFrom(const BoundNodesTree &Other) {
+ Other.getBoundNodes().copyTo(&Bindings);
+ std::copy(Other.branches_begin(), Other.branches_end(),
+ std::back_inserter(RecursiveBindings));
+}
-void BoundNodesTreeBuilder::addMatch(const BoundNodesTree& Bindings) {
- RecursiveBindings.push_back(Bindings);
+/// Called from Sub-Builders via addAsBranc().
+void BoundNodesTreeBuilder::addBranch(const BoundNodesTreeBuilder &SubBuilder) {
+ RecursiveBindings.push_back(SubBuilder.build());
}
BoundNodesTree BoundNodesTreeBuilder::build() const {
Index: unittests/ASTMatchers/ASTMatchersTest.cpp
===================================================================
--- unittests/ASTMatchers/ASTMatchersTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTest.cpp
@@ -3909,5 +3909,144 @@
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(EqualsBoundNodeMatcher, UsingForEachDescendant) {
+ const std::string Fragment =
+ "int f() {"
+ " if (1) {"
+ " int i = 9;"
+ " }"
+ " int j = 10;"
+ " {"
+ " float k = 9.0;"
+ " }"
+ " return 0;"
+ "}";
+
+ 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, DISABLED_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
Index: unittests/ASTMatchers/BoundNodesIteratorTest.cpp
===================================================================
--- /dev/null
+++ unittests/ASTMatchers/BoundNodesIteratorTest.cpp
@@ -0,0 +1,100 @@
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace ast_matchers {
+namespace internal {
+
+class BoundNodeTrueTest : public BoundNodeTest {
+ bool operator()(BoundNodesMap::const_iterator) const { return true; }
+};
+
+TEST(IteratorTest, Basic) {
+ // Construct a tree:
+ //
+ // [a,b]
+ // |
+ // +-[a]
+ // | |
+ // | +-[c,b,a]
+ // | |
+ // | +-[c,d,e]
+ // |
+ // +[d] (Unfinished)
+ //
+ // Where a,b,c,d are Stmt's and e is a BreakStmt.
+
+ BoundNodesTreeBuilder Root(0);
+ Stmt a(Stmt::ContinueStmtClass);
+ Stmt b(Stmt::BreakStmtClass);
+ Stmt c(Stmt::CompoundStmtClass);
+ Stmt d(Stmt::CXXTryStmtClass);
+ SourceLocation Loc;
+ BreakStmt e(Loc);
+ Root.setBinding("a", &a);
+ Root.setBinding("b", &b);
+
+ {
+ BoundNodesTreeBuilder Branch1(&Root);
+ Branch1.setBinding("a", &a);
+ {
+ BoundNodesTreeBuilder Branch2(&Branch1);
+ Branch2.setBinding("c", &c);
+ Branch2.setBinding("b", &b);
+ Branch2.setBinding("a", &a);
+ Branch2.addAsBranch();
+ }
+ {
+ BoundNodesTreeBuilder Branch2(&Branch1);
+ Branch2.setBinding("c", &c);
+ Branch2.setBinding("d", &d);
+ Branch2.setBinding("e", &e);
+ Branch2.addAsBranch();
+ }
+ Branch1.addAsBranch();
+ }
+
+ BoundNodesTreeBuilder Branch1(&Root);
+ Branch1.setBinding("d", &d);
+
+ // This non-templated End iterator is all we need for the tests below.
+ BoundNodesTreeIteratorBase End(0, BoundNodeTrueTest());
+
+ // Basic tests for iterator behaviour.
+ {
+ BoundNodesTreeIterator<Stmt> I(&Branch1, "a");
+
+ ASSERT_NE(I, End);
+ ASSERT_EQ(I, I);
+ ASSERT_EQ(&a, *I);
+ ++I;
+ ASSERT_EQ(&a, *I);
+ ++I;
+ ASSERT_EQ(&a, *I);
+ ++I;
+ ASSERT_EQ(I, End);
+ ASSERT_EQ(0, *I);
+ }
+
+ // Test that node type is used for node identity.
+ {
+ BoundNodesTreeIterator<BreakStmt> I(&Branch1, "a");
+ BoundNodesTreeIterator<BreakStmt> J(&Branch1, "e");
+ ASSERT_EQ(I, End);
+ ASSERT_NE(J, End);
+ ASSERT_EQ(&e, *J);
+ }
+
+ // Root is technically not a completed builder. Since we're starting from
+ // root though, any matches in any un-finished sub-builders are not visible.
+ {
+ BoundNodesTreeIterator<Stmt> I(&Root, "d");
+ ASSERT_EQ(&d, *I);
+ ++I;
+ ASSERT_EQ(End, I);
+ }
+}
+
+} // namespace internal
+} // namespace ast_matchers
+} // namespace clang
Index: unittests/ASTMatchers/CMakeLists.txt
===================================================================
--- unittests/ASTMatchers/CMakeLists.txt
+++ unittests/ASTMatchers/CMakeLists.txt
@@ -7,7 +7,8 @@
)
add_clang_unittest(ASTMatchersTests
- ASTMatchersTest.cpp)
+ ASTMatchersTest.cpp
+ BoundNodesIteratorTest.cpp)
target_link_libraries(ASTMatchersTests
gtest gtest_main clangASTMatchers clangTooling)
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits