aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Benzaquen <sbenza@google.com>2013-06-03 19:31:08 +0000
committerSamuel Benzaquen <sbenza@google.com>2013-06-03 19:31:08 +0000
commit4f37d925927dfdd0c770702ffb22de38fc2007dc (patch)
tree25c980f2046e9c716f07a6ecb001cb6145264910
parent75ddd1465edeb0ced330263b3d6b30f706a4d33d (diff)
downloadclang-4f37d925927dfdd0c770702ffb22de38fc2007dc.tar.gz
Add support for .bind("foo") expressions on the dynamic matchers.
Summary: Add support on the parser, registry, and DynTypedMatcher for binding IDs dynamically. Reviewers: klimek CC: cfe-commits, revane Differential Revision: http://llvm-reviews.chandlerc.com/D911 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@183144 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/ASTMatchers/ASTMatchersInternal.h17
-rw-r--r--include/clang/ASTMatchers/Dynamic/Diagnostics.h5
-rw-r--r--include/clang/ASTMatchers/Dynamic/Parser.h12
-rw-r--r--include/clang/ASTMatchers/Dynamic/Registry.h12
-rw-r--r--lib/ASTMatchers/ASTMatchersInternal.cpp4
-rw-r--r--lib/ASTMatchers/Dynamic/Diagnostics.cpp6
-rw-r--r--lib/ASTMatchers/Dynamic/Parser.cpp72
-rw-r--r--lib/ASTMatchers/Dynamic/Registry.cpp17
-rw-r--r--unittests/ASTMatchers/Dynamic/ParserTest.cpp35
9 files changed, 162 insertions, 18 deletions
diff --git a/include/clang/ASTMatchers/ASTMatchersInternal.h b/include/clang/ASTMatchers/ASTMatchersInternal.h
index bc4ddce36f..6428ad85a7 100644
--- a/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -232,7 +232,7 @@ private:
/// on the actual node, or return false if it is not convertible.
class DynTypedMatcher {
public:
- virtual ~DynTypedMatcher() {}
+ virtual ~DynTypedMatcher();
/// \brief Returns true if the matcher matches the given \c DynNode.
virtual bool matches(const ast_type_traits::DynTypedNode DynNode,
@@ -244,6 +244,11 @@ public:
/// \brief Returns a unique ID for the matcher.
virtual uint64_t getID() const = 0;
+
+ /// \brief Bind the specified \p ID to the matcher.
+ /// \return A new matcher with the \p ID bound to it if this matcher supports
+ /// binding. Otherwise, returns NULL. Returns NULL by default.
+ virtual DynTypedMatcher* tryBind(StringRef ID) const;
};
/// \brief Wrapper of a MatcherInterface<T> *that allows copying.
@@ -806,6 +811,16 @@ public:
Matcher<T> bind(StringRef ID) const {
return Matcher<T>(new IdMatcher<T>(ID, *this));
}
+
+ /// \brief Makes a copy of this matcher object.
+ virtual BindableMatcher<T>* clone() const {
+ return new BindableMatcher<T>(*this);
+ }
+
+ /// \brief Bind the specified \c ID to the matcher.
+ virtual Matcher<T>* tryBind(StringRef ID) const {
+ return new Matcher<T>(bind(ID));
+ }
};
/// \brief Matches nodes of type T that have child nodes of type ChildT for
diff --git a/include/clang/ASTMatchers/Dynamic/Diagnostics.h b/include/clang/ASTMatchers/Dynamic/Diagnostics.h
index 417bc67967..38e87ce37a 100644
--- a/include/clang/ASTMatchers/Dynamic/Diagnostics.h
+++ b/include/clang/ASTMatchers/Dynamic/Diagnostics.h
@@ -57,6 +57,7 @@ class Diagnostics {
ET_RegistryNotFound = 1,
ET_RegistryWrongArgCount = 2,
ET_RegistryWrongArgType = 3,
+ ET_RegistryNotBindable = 4,
ET_ParserStringError = 100,
ET_ParserMatcherArgFailure = 101,
@@ -66,7 +67,9 @@ class Diagnostics {
ET_ParserNoComma = 105,
ET_ParserNoCode = 106,
ET_ParserNotAMatcher = 107,
- ET_ParserInvalidToken = 108
+ ET_ParserInvalidToken = 108,
+ ET_ParserMalformedBindExpr = 109,
+ ET_ParserTrailingCode = 110
};
/// \brief Helper stream class.
diff --git a/include/clang/ASTMatchers/Dynamic/Parser.h b/include/clang/ASTMatchers/Dynamic/Parser.h
index f981c6055e..b6cd4afebe 100644
--- a/include/clang/ASTMatchers/Dynamic/Parser.h
+++ b/include/clang/ASTMatchers/Dynamic/Parser.h
@@ -20,7 +20,8 @@
/// Grammar for the expressions supported:
/// <Expression> := <StringLiteral> | <MatcherExpression>
/// <StringLiteral> := "quoted string"
-/// <MatcherExpression> := <MatcherName>(<ArgumentList>)
+/// <MatcherExpression> := <MatcherName>(<ArgumentList>) |
+/// <MatcherName>(<ArgumentList>).bind(<StringLiteral>)
/// <MatcherName> := [a-zA-Z]+
/// <ArgumentList> := <Expression> | <Expression>,<ArgumentList>
/// \endcode
@@ -66,15 +67,18 @@ public:
/// \param NameRange The location of the name in the matcher source.
/// Useful for error reporting.
///
+ /// \param BindID The ID to use to bind the matcher, or a null \c StringRef
+ /// if no ID is specified.
+ ///
/// \param Args The argument list for the matcher.
///
/// \return The matcher object constructed by the processor, or NULL
/// if an error occurred. In that case, \c Error will contain a
/// description of the error.
/// The caller takes ownership of the DynTypedMatcher object returned.
- virtual DynTypedMatcher *
- actOnMatcherExpression(StringRef MatcherName, const SourceRange &NameRange,
- ArrayRef<ParserValue> Args, Diagnostics *Error) = 0;
+ virtual DynTypedMatcher *actOnMatcherExpression(
+ StringRef MatcherName, const SourceRange &NameRange, StringRef BindID,
+ ArrayRef<ParserValue> Args, Diagnostics *Error) = 0;
};
/// \brief Parse a matcher expression, creating matchers from the registry.
diff --git a/include/clang/ASTMatchers/Dynamic/Registry.h b/include/clang/ASTMatchers/Dynamic/Registry.h
index b092ed8fe1..dede8df776 100644
--- a/include/clang/ASTMatchers/Dynamic/Registry.h
+++ b/include/clang/ASTMatchers/Dynamic/Registry.h
@@ -52,6 +52,18 @@ public:
ArrayRef<ParserValue> Args,
Diagnostics *Error);
+ /// \brief Construct a matcher from the registry and bind it.
+ ///
+ /// Similar the \c constructMatcher() above, but it then tries to bind the
+ /// matcher to the specified \c BindID.
+ /// If the matcher is not bindable, it sets an error in \c Error and returns
+ /// \c NULL.
+ static DynTypedMatcher *constructBoundMatcher(StringRef MatcherName,
+ const SourceRange &NameRange,
+ StringRef BindID,
+ ArrayRef<ParserValue> Args,
+ Diagnostics *Error);
+
private:
Registry() LLVM_DELETED_FUNCTION;
};
diff --git a/lib/ASTMatchers/ASTMatchersInternal.cpp b/lib/ASTMatchers/ASTMatchersInternal.cpp
index f1a9ff2e09..3144261612 100644
--- a/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -82,6 +82,10 @@ BoundNodesTree BoundNodesTreeBuilder::build() const {
return BoundNodesTree(Bindings, RecursiveBindings);
}
+DynTypedMatcher::~DynTypedMatcher() {}
+
+DynTypedMatcher *DynTypedMatcher::tryBind(StringRef ID) const { return NULL; }
+
} // end namespace internal
} // end namespace ast_matchers
} // end namespace clang
diff --git a/lib/ASTMatchers/Dynamic/Diagnostics.cpp b/lib/ASTMatchers/Dynamic/Diagnostics.cpp
index fb3cac370f..4b01b997a1 100644
--- a/lib/ASTMatchers/Dynamic/Diagnostics.cpp
+++ b/lib/ASTMatchers/Dynamic/Diagnostics.cpp
@@ -37,6 +37,8 @@ StringRef ErrorTypeToString(Diagnostics::ErrorType Type) {
return "Incorrect argument count. (Expected = $0) != (Actual = $1)";
case Diagnostics::ET_RegistryWrongArgType:
return "Incorrect type on function $0 for arg $1.";
+ case Diagnostics::ET_RegistryNotBindable:
+ return "Matcher does not support binding.";
case Diagnostics::ET_ParserStringError:
return "Error parsing string token: <$0>";
@@ -56,6 +58,10 @@ StringRef ErrorTypeToString(Diagnostics::ErrorType Type) {
return "Input value is not a matcher expression.";
case Diagnostics::ET_ParserInvalidToken:
return "Invalid token <$0> found when looking for a value.";
+ case Diagnostics::ET_ParserMalformedBindExpr:
+ return "Malformed bind() expression.";
+ case Diagnostics::ET_ParserTrailingCode:
+ return "Expected end of code.";
case Diagnostics::ET_None:
return "<N/A>";
diff --git a/lib/ASTMatchers/Dynamic/Parser.cpp b/lib/ASTMatchers/Dynamic/Parser.cpp
index 1678820da0..1ed40f3050 100644
--- a/lib/ASTMatchers/Dynamic/Parser.cpp
+++ b/lib/ASTMatchers/Dynamic/Parser.cpp
@@ -32,12 +32,16 @@ struct Parser::TokenInfo {
TK_OpenParen = 1,
TK_CloseParen = 2,
TK_Comma = 3,
- TK_Literal = 4,
- TK_Ident = 5,
- TK_InvalidChar = 6,
- TK_Error = 7
+ TK_Period = 4,
+ TK_Literal = 5,
+ TK_Ident = 6,
+ TK_InvalidChar = 7,
+ TK_Error = 8
};
+ /// \brief Some known identifiers.
+ static const char* const ID_Bind;
+
TokenInfo() : Text(), Kind(TK_Eof), Range(), Value() {}
StringRef Text;
@@ -46,6 +50,8 @@ struct Parser::TokenInfo {
VariantValue Value;
};
+const char* const Parser::TokenInfo::ID_Bind = "bind";
+
/// \brief Simple tokenizer for the parser.
class Parser::CodeTokenizer {
public:
@@ -84,6 +90,11 @@ private:
Result.Text = Code.substr(0, 1);
Code = Code.drop_front();
break;
+ case '.':
+ Result.Kind = TokenInfo::TK_Period;
+ Result.Text = Code.substr(0, 1);
+ Code = Code.drop_front();
+ break;
case '(':
Result.Kind = TokenInfo::TK_OpenParen;
Result.Text = Code.substr(0, 1);
@@ -234,11 +245,43 @@ bool Parser::parseMatcherExpressionImpl(VariantValue *Value) {
return false;
}
+ std::string BindID;
+ if (Tokenizer->peekNextToken().Kind == TokenInfo::TK_Period) {
+ // Parse .bind("foo")
+ Tokenizer->consumeNextToken(); // consume the period.
+ const TokenInfo BindToken = Tokenizer->consumeNextToken();
+ const TokenInfo OpenToken = Tokenizer->consumeNextToken();
+ const TokenInfo IDToken = Tokenizer->consumeNextToken();
+ const TokenInfo CloseToken = Tokenizer->consumeNextToken();
+
+ // TODO: We could use different error codes for each/some to be more
+ // explicit about the syntax error.
+ if (BindToken.Kind != TokenInfo::TK_Ident ||
+ BindToken.Text != TokenInfo::ID_Bind) {
+ Error->pushErrorFrame(BindToken.Range, Error->ET_ParserMalformedBindExpr);
+ return false;
+ }
+ if (OpenToken.Kind != TokenInfo::TK_OpenParen) {
+ Error->pushErrorFrame(OpenToken.Range, Error->ET_ParserMalformedBindExpr);
+ return false;
+ }
+ if (IDToken.Kind != TokenInfo::TK_Literal || !IDToken.Value.isString()) {
+ Error->pushErrorFrame(IDToken.Range, Error->ET_ParserMalformedBindExpr);
+ return false;
+ }
+ if (CloseToken.Kind != TokenInfo::TK_CloseParen) {
+ Error->pushErrorFrame(CloseToken.Range,
+ Error->ET_ParserMalformedBindExpr);
+ return false;
+ }
+ BindID = IDToken.Value.getString();
+ }
+
// Merge the start and end infos.
SourceRange MatcherRange = NameToken.Range;
MatcherRange.End = EndToken.Range.End;
- DynTypedMatcher *Result =
- S->actOnMatcherExpression(NameToken.Text, MatcherRange, Args, Error);
+ DynTypedMatcher *Result = S->actOnMatcherExpression(
+ NameToken.Text, MatcherRange, BindID, Args, Error);
if (Result == NULL) {
Error->pushErrorFrame(NameToken.Range, Error->ET_ParserMatcherFailure)
<< NameToken.Text;
@@ -271,6 +314,7 @@ bool Parser::parseExpressionImpl(VariantValue *Value) {
case TokenInfo::TK_OpenParen:
case TokenInfo::TK_CloseParen:
case TokenInfo::TK_Comma:
+ case TokenInfo::TK_Period:
case TokenInfo::TK_InvalidChar:
const TokenInfo Token = Tokenizer->consumeNextToken();
Error->pushErrorFrame(Token.Range, Error->ET_ParserInvalidToken)
@@ -290,9 +334,15 @@ public:
virtual ~RegistrySema() {}
DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName,
const SourceRange &NameRange,
+ StringRef BindID,
ArrayRef<ParserValue> Args,
Diagnostics *Error) {
- return Registry::constructMatcher(MatcherName, NameRange, Args, Error);
+ if (BindID.empty()) {
+ return Registry::constructMatcher(MatcherName, NameRange, Args, Error);
+ } else {
+ return Registry::constructBoundMatcher(MatcherName, NameRange, BindID,
+ Args, Error);
+ }
}
};
@@ -305,7 +355,13 @@ bool Parser::parseExpression(StringRef Code, VariantValue *Value,
bool Parser::parseExpression(StringRef Code, Sema *S,
VariantValue *Value, Diagnostics *Error) {
CodeTokenizer Tokenizer(Code, Error);
- return Parser(&Tokenizer, S, Error).parseExpressionImpl(Value);
+ if (!Parser(&Tokenizer, S, Error).parseExpressionImpl(Value)) return false;
+ if (Tokenizer.peekNextToken().Kind != TokenInfo::TK_Eof) {
+ Error->pushErrorFrame(Tokenizer.peekNextToken().Range,
+ Error->ET_ParserTrailingCode);
+ return false;
+ }
+ return true;
}
DynTypedMatcher *Parser::parseMatcherExpression(StringRef Code,
diff --git a/lib/ASTMatchers/Dynamic/Registry.cpp b/lib/ASTMatchers/Dynamic/Registry.cpp
index 34a230b872..6e543fc850 100644
--- a/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -148,6 +148,23 @@ DynTypedMatcher *Registry::constructMatcher(StringRef MatcherName,
return it->second->run(NameRange, Args, Error);
}
+// static
+DynTypedMatcher *Registry::constructBoundMatcher(StringRef MatcherName,
+ const SourceRange &NameRange,
+ StringRef BindID,
+ ArrayRef<ParserValue> Args,
+ Diagnostics *Error) {
+ OwningPtr<DynTypedMatcher> Out(
+ constructMatcher(MatcherName, NameRange, Args, Error));
+ if (!Out) return NULL;
+ DynTypedMatcher *Bound = Out->tryBind(BindID);
+ if (!Bound) {
+ Error->pushErrorFrame(NameRange, Error->ET_RegistryNotBindable);
+ return NULL;
+ }
+ return Bound;
+}
+
} // namespace dynamic
} // namespace ast_matchers
} // namespace clang
diff --git a/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/unittests/ASTMatchers/Dynamic/ParserTest.cpp
index 41f522856d..310108a38c 100644
--- a/unittests/ASTMatchers/Dynamic/ParserTest.cpp
+++ b/unittests/ASTMatchers/Dynamic/ParserTest.cpp
@@ -24,6 +24,8 @@ namespace {
class DummyDynTypedMatcher : public DynTypedMatcher {
public:
DummyDynTypedMatcher(uint64_t ID) : ID(ID) {}
+ DummyDynTypedMatcher(uint64_t ID, StringRef BoundID)
+ : ID(ID), BoundID(BoundID) {}
typedef ast_matchers::internal::ASTMatchFinder ASTMatchFinder;
typedef ast_matchers::internal::BoundNodesTreeBuilder BoundNodesTreeBuilder;
@@ -35,14 +37,21 @@ public:
/// \brief Makes a copy of this matcher object.
virtual DynTypedMatcher *clone() const {
- return new DummyDynTypedMatcher(ID);
+ return new DummyDynTypedMatcher(*this);
}
/// \brief Returns a unique ID for the matcher.
virtual uint64_t getID() const { return ID; }
+ virtual DynTypedMatcher* tryBind(StringRef BoundID) const {
+ return new DummyDynTypedMatcher(ID, BoundID);
+ }
+
+ StringRef boundID() const { return BoundID; }
+
private:
uint64_t ID;
+ std::string BoundID;
};
class MockSema : public Parser::Sema {
@@ -65,17 +74,20 @@ public:
DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName,
const SourceRange &NameRange,
+ StringRef BindID,
ArrayRef<ParserValue> Args,
Diagnostics *Error) {
- MatcherInfo ToStore = { MatcherName, NameRange, Args };
+ MatcherInfo ToStore = { MatcherName, NameRange, Args, BindID };
Matchers.push_back(ToStore);
- return new DummyDynTypedMatcher(ExpectedMatchers[MatcherName]);
+ DummyDynTypedMatcher Matcher(ExpectedMatchers[MatcherName]);
+ return Matcher.tryBind(BindID);
}
struct MatcherInfo {
StringRef MatcherName;
SourceRange NameRange;
std::vector<ParserValue> Args;
+ std::string BoundID;
};
std::vector<std::string> Errors;
@@ -110,13 +122,15 @@ TEST(ParserTest, ParseMatcher) {
const uint64_t ExpectedFoo = Sema.expectMatcher("Foo");
const uint64_t ExpectedBar = Sema.expectMatcher("Bar");
const uint64_t ExpectedBaz = Sema.expectMatcher("Baz");
- Sema.parse(" Foo ( Bar (), Baz( \n \"B A,Z\") ) ");
+ Sema.parse(" Foo ( Bar (), Baz( \n \"B A,Z\") ) .bind( \"Yo!\") ");
for (size_t i = 0, e = Sema.Errors.size(); i != e; ++i) {
EXPECT_EQ("", Sema.Errors[i]);
}
EXPECT_EQ(1ULL, Sema.Values.size());
EXPECT_EQ(ExpectedFoo, Sema.Values[0].getMatcher().getID());
+ EXPECT_EQ("Yo!", static_cast<const DummyDynTypedMatcher &>(
+ Sema.Values[0].getMatcher()).boundID());
EXPECT_EQ(3ULL, Sema.Matchers.size());
const MockSema::MatcherInfo Bar = Sema.Matchers[0];
@@ -136,6 +150,7 @@ TEST(ParserTest, ParseMatcher) {
EXPECT_EQ(2ULL, Foo.Args.size());
EXPECT_EQ(ExpectedBar, Foo.Args[0].Value.getMatcher().getID());
EXPECT_EQ(ExpectedBaz, Foo.Args[1].Value.getMatcher().getID());
+ EXPECT_EQ("Yo!", Foo.BoundID);
}
using ast_matchers::internal::Matcher;
@@ -186,6 +201,18 @@ TEST(ParserTest, Errors) {
EXPECT_EQ("1:1: Error parsing argument 1 for matcher Foo.\n"
"1:5: Invalid token <(> found when looking for a value.",
ParseWithError("Foo(("));
+ EXPECT_EQ("1:7: Expected end of code.", ParseWithError("expr()a"));
+ EXPECT_EQ("1:11: Malformed bind() expression.",
+ ParseWithError("isArrow().biind"));
+ EXPECT_EQ("1:15: Malformed bind() expression.",
+ ParseWithError("isArrow().bind"));
+ EXPECT_EQ("1:16: Malformed bind() expression.",
+ ParseWithError("isArrow().bind(foo"));
+ EXPECT_EQ("1:21: Malformed bind() expression.",
+ ParseWithError("isArrow().bind(\"foo\""));
+ EXPECT_EQ("1:1: Error building matcher isArrow.\n"
+ "1:1: Matcher does not support binding.",
+ ParseWithError("isArrow().bind(\"foo\")"));
}
} // end anonymous namespace