forked from OSchip/llvm-project
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 llvm-svn: 183144
This commit is contained in:
parent
f102438f3a
commit
31edb51a4f
|
@ -232,7 +232,7 @@ private:
|
||||||
/// on the actual node, or return false if it is not convertible.
|
/// on the actual node, or return false if it is not convertible.
|
||||||
class DynTypedMatcher {
|
class DynTypedMatcher {
|
||||||
public:
|
public:
|
||||||
virtual ~DynTypedMatcher() {}
|
virtual ~DynTypedMatcher();
|
||||||
|
|
||||||
/// \brief Returns true if the matcher matches the given \c DynNode.
|
/// \brief Returns true if the matcher matches the given \c DynNode.
|
||||||
virtual bool matches(const ast_type_traits::DynTypedNode DynNode,
|
virtual bool matches(const ast_type_traits::DynTypedNode DynNode,
|
||||||
|
@ -244,6 +244,11 @@ public:
|
||||||
|
|
||||||
/// \brief Returns a unique ID for the matcher.
|
/// \brief Returns a unique ID for the matcher.
|
||||||
virtual uint64_t getID() const = 0;
|
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.
|
/// \brief Wrapper of a MatcherInterface<T> *that allows copying.
|
||||||
|
@ -806,6 +811,16 @@ public:
|
||||||
Matcher<T> bind(StringRef ID) const {
|
Matcher<T> bind(StringRef ID) const {
|
||||||
return Matcher<T>(new IdMatcher<T>(ID, *this));
|
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
|
/// \brief Matches nodes of type T that have child nodes of type ChildT for
|
||||||
|
|
|
@ -57,6 +57,7 @@ class Diagnostics {
|
||||||
ET_RegistryNotFound = 1,
|
ET_RegistryNotFound = 1,
|
||||||
ET_RegistryWrongArgCount = 2,
|
ET_RegistryWrongArgCount = 2,
|
||||||
ET_RegistryWrongArgType = 3,
|
ET_RegistryWrongArgType = 3,
|
||||||
|
ET_RegistryNotBindable = 4,
|
||||||
|
|
||||||
ET_ParserStringError = 100,
|
ET_ParserStringError = 100,
|
||||||
ET_ParserMatcherArgFailure = 101,
|
ET_ParserMatcherArgFailure = 101,
|
||||||
|
@ -66,7 +67,9 @@ class Diagnostics {
|
||||||
ET_ParserNoComma = 105,
|
ET_ParserNoComma = 105,
|
||||||
ET_ParserNoCode = 106,
|
ET_ParserNoCode = 106,
|
||||||
ET_ParserNotAMatcher = 107,
|
ET_ParserNotAMatcher = 107,
|
||||||
ET_ParserInvalidToken = 108
|
ET_ParserInvalidToken = 108,
|
||||||
|
ET_ParserMalformedBindExpr = 109,
|
||||||
|
ET_ParserTrailingCode = 110
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Helper stream class.
|
/// \brief Helper stream class.
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
/// Grammar for the expressions supported:
|
/// Grammar for the expressions supported:
|
||||||
/// <Expression> := <StringLiteral> | <MatcherExpression>
|
/// <Expression> := <StringLiteral> | <MatcherExpression>
|
||||||
/// <StringLiteral> := "quoted string"
|
/// <StringLiteral> := "quoted string"
|
||||||
/// <MatcherExpression> := <MatcherName>(<ArgumentList>)
|
/// <MatcherExpression> := <MatcherName>(<ArgumentList>) |
|
||||||
|
/// <MatcherName>(<ArgumentList>).bind(<StringLiteral>)
|
||||||
/// <MatcherName> := [a-zA-Z]+
|
/// <MatcherName> := [a-zA-Z]+
|
||||||
/// <ArgumentList> := <Expression> | <Expression>,<ArgumentList>
|
/// <ArgumentList> := <Expression> | <Expression>,<ArgumentList>
|
||||||
/// \endcode
|
/// \endcode
|
||||||
|
@ -66,15 +67,18 @@ public:
|
||||||
/// \param NameRange The location of the name in the matcher source.
|
/// \param NameRange The location of the name in the matcher source.
|
||||||
/// Useful for error reporting.
|
/// 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.
|
/// \param Args The argument list for the matcher.
|
||||||
///
|
///
|
||||||
/// \return The matcher object constructed by the processor, or NULL
|
/// \return The matcher object constructed by the processor, or NULL
|
||||||
/// if an error occurred. In that case, \c Error will contain a
|
/// if an error occurred. In that case, \c Error will contain a
|
||||||
/// description of the error.
|
/// description of the error.
|
||||||
/// The caller takes ownership of the DynTypedMatcher object returned.
|
/// The caller takes ownership of the DynTypedMatcher object returned.
|
||||||
virtual DynTypedMatcher *
|
virtual DynTypedMatcher *actOnMatcherExpression(
|
||||||
actOnMatcherExpression(StringRef MatcherName, const SourceRange &NameRange,
|
StringRef MatcherName, const SourceRange &NameRange, StringRef BindID,
|
||||||
ArrayRef<ParserValue> Args, Diagnostics *Error) = 0;
|
ArrayRef<ParserValue> Args, Diagnostics *Error) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Parse a matcher expression, creating matchers from the registry.
|
/// \brief Parse a matcher expression, creating matchers from the registry.
|
||||||
|
|
|
@ -52,6 +52,18 @@ public:
|
||||||
ArrayRef<ParserValue> Args,
|
ArrayRef<ParserValue> Args,
|
||||||
Diagnostics *Error);
|
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:
|
private:
|
||||||
Registry() LLVM_DELETED_FUNCTION;
|
Registry() LLVM_DELETED_FUNCTION;
|
||||||
};
|
};
|
||||||
|
|
|
@ -82,6 +82,10 @@ BoundNodesTree BoundNodesTreeBuilder::build() const {
|
||||||
return BoundNodesTree(Bindings, RecursiveBindings);
|
return BoundNodesTree(Bindings, RecursiveBindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DynTypedMatcher::~DynTypedMatcher() {}
|
||||||
|
|
||||||
|
DynTypedMatcher *DynTypedMatcher::tryBind(StringRef ID) const { return NULL; }
|
||||||
|
|
||||||
} // end namespace internal
|
} // end namespace internal
|
||||||
} // end namespace ast_matchers
|
} // end namespace ast_matchers
|
||||||
} // end namespace clang
|
} // end namespace clang
|
||||||
|
|
|
@ -37,6 +37,8 @@ StringRef ErrorTypeToString(Diagnostics::ErrorType Type) {
|
||||||
return "Incorrect argument count. (Expected = $0) != (Actual = $1)";
|
return "Incorrect argument count. (Expected = $0) != (Actual = $1)";
|
||||||
case Diagnostics::ET_RegistryWrongArgType:
|
case Diagnostics::ET_RegistryWrongArgType:
|
||||||
return "Incorrect type on function $0 for arg $1.";
|
return "Incorrect type on function $0 for arg $1.";
|
||||||
|
case Diagnostics::ET_RegistryNotBindable:
|
||||||
|
return "Matcher does not support binding.";
|
||||||
|
|
||||||
case Diagnostics::ET_ParserStringError:
|
case Diagnostics::ET_ParserStringError:
|
||||||
return "Error parsing string token: <$0>";
|
return "Error parsing string token: <$0>";
|
||||||
|
@ -56,6 +58,10 @@ StringRef ErrorTypeToString(Diagnostics::ErrorType Type) {
|
||||||
return "Input value is not a matcher expression.";
|
return "Input value is not a matcher expression.";
|
||||||
case Diagnostics::ET_ParserInvalidToken:
|
case Diagnostics::ET_ParserInvalidToken:
|
||||||
return "Invalid token <$0> found when looking for a value.";
|
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:
|
case Diagnostics::ET_None:
|
||||||
return "<N/A>";
|
return "<N/A>";
|
||||||
|
|
|
@ -32,12 +32,16 @@ struct Parser::TokenInfo {
|
||||||
TK_OpenParen = 1,
|
TK_OpenParen = 1,
|
||||||
TK_CloseParen = 2,
|
TK_CloseParen = 2,
|
||||||
TK_Comma = 3,
|
TK_Comma = 3,
|
||||||
TK_Literal = 4,
|
TK_Period = 4,
|
||||||
TK_Ident = 5,
|
TK_Literal = 5,
|
||||||
TK_InvalidChar = 6,
|
TK_Ident = 6,
|
||||||
TK_Error = 7
|
TK_InvalidChar = 7,
|
||||||
|
TK_Error = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \brief Some known identifiers.
|
||||||
|
static const char* const ID_Bind;
|
||||||
|
|
||||||
TokenInfo() : Text(), Kind(TK_Eof), Range(), Value() {}
|
TokenInfo() : Text(), Kind(TK_Eof), Range(), Value() {}
|
||||||
|
|
||||||
StringRef Text;
|
StringRef Text;
|
||||||
|
@ -46,6 +50,8 @@ struct Parser::TokenInfo {
|
||||||
VariantValue Value;
|
VariantValue Value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char* const Parser::TokenInfo::ID_Bind = "bind";
|
||||||
|
|
||||||
/// \brief Simple tokenizer for the parser.
|
/// \brief Simple tokenizer for the parser.
|
||||||
class Parser::CodeTokenizer {
|
class Parser::CodeTokenizer {
|
||||||
public:
|
public:
|
||||||
|
@ -84,6 +90,11 @@ private:
|
||||||
Result.Text = Code.substr(0, 1);
|
Result.Text = Code.substr(0, 1);
|
||||||
Code = Code.drop_front();
|
Code = Code.drop_front();
|
||||||
break;
|
break;
|
||||||
|
case '.':
|
||||||
|
Result.Kind = TokenInfo::TK_Period;
|
||||||
|
Result.Text = Code.substr(0, 1);
|
||||||
|
Code = Code.drop_front();
|
||||||
|
break;
|
||||||
case '(':
|
case '(':
|
||||||
Result.Kind = TokenInfo::TK_OpenParen;
|
Result.Kind = TokenInfo::TK_OpenParen;
|
||||||
Result.Text = Code.substr(0, 1);
|
Result.Text = Code.substr(0, 1);
|
||||||
|
@ -234,11 +245,43 @@ bool Parser::parseMatcherExpressionImpl(VariantValue *Value) {
|
||||||
return false;
|
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.
|
// Merge the start and end infos.
|
||||||
SourceRange MatcherRange = NameToken.Range;
|
SourceRange MatcherRange = NameToken.Range;
|
||||||
MatcherRange.End = EndToken.Range.End;
|
MatcherRange.End = EndToken.Range.End;
|
||||||
DynTypedMatcher *Result =
|
DynTypedMatcher *Result = S->actOnMatcherExpression(
|
||||||
S->actOnMatcherExpression(NameToken.Text, MatcherRange, Args, Error);
|
NameToken.Text, MatcherRange, BindID, Args, Error);
|
||||||
if (Result == NULL) {
|
if (Result == NULL) {
|
||||||
Error->pushErrorFrame(NameToken.Range, Error->ET_ParserMatcherFailure)
|
Error->pushErrorFrame(NameToken.Range, Error->ET_ParserMatcherFailure)
|
||||||
<< NameToken.Text;
|
<< NameToken.Text;
|
||||||
|
@ -271,6 +314,7 @@ bool Parser::parseExpressionImpl(VariantValue *Value) {
|
||||||
case TokenInfo::TK_OpenParen:
|
case TokenInfo::TK_OpenParen:
|
||||||
case TokenInfo::TK_CloseParen:
|
case TokenInfo::TK_CloseParen:
|
||||||
case TokenInfo::TK_Comma:
|
case TokenInfo::TK_Comma:
|
||||||
|
case TokenInfo::TK_Period:
|
||||||
case TokenInfo::TK_InvalidChar:
|
case TokenInfo::TK_InvalidChar:
|
||||||
const TokenInfo Token = Tokenizer->consumeNextToken();
|
const TokenInfo Token = Tokenizer->consumeNextToken();
|
||||||
Error->pushErrorFrame(Token.Range, Error->ET_ParserInvalidToken)
|
Error->pushErrorFrame(Token.Range, Error->ET_ParserInvalidToken)
|
||||||
|
@ -290,9 +334,15 @@ public:
|
||||||
virtual ~RegistrySema() {}
|
virtual ~RegistrySema() {}
|
||||||
DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName,
|
DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName,
|
||||||
const SourceRange &NameRange,
|
const SourceRange &NameRange,
|
||||||
|
StringRef BindID,
|
||||||
ArrayRef<ParserValue> Args,
|
ArrayRef<ParserValue> Args,
|
||||||
Diagnostics *Error) {
|
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,
|
bool Parser::parseExpression(StringRef Code, Sema *S,
|
||||||
VariantValue *Value, Diagnostics *Error) {
|
VariantValue *Value, Diagnostics *Error) {
|
||||||
CodeTokenizer Tokenizer(Code, 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,
|
DynTypedMatcher *Parser::parseMatcherExpression(StringRef Code,
|
||||||
|
|
|
@ -148,6 +148,23 @@ DynTypedMatcher *Registry::constructMatcher(StringRef MatcherName,
|
||||||
return it->second->run(NameRange, Args, Error);
|
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 dynamic
|
||||||
} // namespace ast_matchers
|
} // namespace ast_matchers
|
||||||
} // namespace clang
|
} // namespace clang
|
||||||
|
|
|
@ -24,6 +24,8 @@ namespace {
|
||||||
class DummyDynTypedMatcher : public DynTypedMatcher {
|
class DummyDynTypedMatcher : public DynTypedMatcher {
|
||||||
public:
|
public:
|
||||||
DummyDynTypedMatcher(uint64_t ID) : ID(ID) {}
|
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::ASTMatchFinder ASTMatchFinder;
|
||||||
typedef ast_matchers::internal::BoundNodesTreeBuilder BoundNodesTreeBuilder;
|
typedef ast_matchers::internal::BoundNodesTreeBuilder BoundNodesTreeBuilder;
|
||||||
|
@ -35,14 +37,21 @@ public:
|
||||||
|
|
||||||
/// \brief Makes a copy of this matcher object.
|
/// \brief Makes a copy of this matcher object.
|
||||||
virtual DynTypedMatcher *clone() const {
|
virtual DynTypedMatcher *clone() const {
|
||||||
return new DummyDynTypedMatcher(ID);
|
return new DummyDynTypedMatcher(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Returns a unique ID for the matcher.
|
/// \brief Returns a unique ID for the matcher.
|
||||||
virtual uint64_t getID() const { return ID; }
|
virtual uint64_t getID() const { return ID; }
|
||||||
|
|
||||||
|
virtual DynTypedMatcher* tryBind(StringRef BoundID) const {
|
||||||
|
return new DummyDynTypedMatcher(ID, BoundID);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringRef boundID() const { return BoundID; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t ID;
|
uint64_t ID;
|
||||||
|
std::string BoundID;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MockSema : public Parser::Sema {
|
class MockSema : public Parser::Sema {
|
||||||
|
@ -65,17 +74,20 @@ public:
|
||||||
|
|
||||||
DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName,
|
DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName,
|
||||||
const SourceRange &NameRange,
|
const SourceRange &NameRange,
|
||||||
|
StringRef BindID,
|
||||||
ArrayRef<ParserValue> Args,
|
ArrayRef<ParserValue> Args,
|
||||||
Diagnostics *Error) {
|
Diagnostics *Error) {
|
||||||
MatcherInfo ToStore = { MatcherName, NameRange, Args };
|
MatcherInfo ToStore = { MatcherName, NameRange, Args, BindID };
|
||||||
Matchers.push_back(ToStore);
|
Matchers.push_back(ToStore);
|
||||||
return new DummyDynTypedMatcher(ExpectedMatchers[MatcherName]);
|
DummyDynTypedMatcher Matcher(ExpectedMatchers[MatcherName]);
|
||||||
|
return Matcher.tryBind(BindID);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MatcherInfo {
|
struct MatcherInfo {
|
||||||
StringRef MatcherName;
|
StringRef MatcherName;
|
||||||
SourceRange NameRange;
|
SourceRange NameRange;
|
||||||
std::vector<ParserValue> Args;
|
std::vector<ParserValue> Args;
|
||||||
|
std::string BoundID;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<std::string> Errors;
|
std::vector<std::string> Errors;
|
||||||
|
@ -110,13 +122,15 @@ TEST(ParserTest, ParseMatcher) {
|
||||||
const uint64_t ExpectedFoo = Sema.expectMatcher("Foo");
|
const uint64_t ExpectedFoo = Sema.expectMatcher("Foo");
|
||||||
const uint64_t ExpectedBar = Sema.expectMatcher("Bar");
|
const uint64_t ExpectedBar = Sema.expectMatcher("Bar");
|
||||||
const uint64_t ExpectedBaz = Sema.expectMatcher("Baz");
|
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) {
|
for (size_t i = 0, e = Sema.Errors.size(); i != e; ++i) {
|
||||||
EXPECT_EQ("", Sema.Errors[i]);
|
EXPECT_EQ("", Sema.Errors[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_EQ(1ULL, Sema.Values.size());
|
EXPECT_EQ(1ULL, Sema.Values.size());
|
||||||
EXPECT_EQ(ExpectedFoo, Sema.Values[0].getMatcher().getID());
|
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());
|
EXPECT_EQ(3ULL, Sema.Matchers.size());
|
||||||
const MockSema::MatcherInfo Bar = Sema.Matchers[0];
|
const MockSema::MatcherInfo Bar = Sema.Matchers[0];
|
||||||
|
@ -136,6 +150,7 @@ TEST(ParserTest, ParseMatcher) {
|
||||||
EXPECT_EQ(2ULL, Foo.Args.size());
|
EXPECT_EQ(2ULL, Foo.Args.size());
|
||||||
EXPECT_EQ(ExpectedBar, Foo.Args[0].Value.getMatcher().getID());
|
EXPECT_EQ(ExpectedBar, Foo.Args[0].Value.getMatcher().getID());
|
||||||
EXPECT_EQ(ExpectedBaz, Foo.Args[1].Value.getMatcher().getID());
|
EXPECT_EQ(ExpectedBaz, Foo.Args[1].Value.getMatcher().getID());
|
||||||
|
EXPECT_EQ("Yo!", Foo.BoundID);
|
||||||
}
|
}
|
||||||
|
|
||||||
using ast_matchers::internal::Matcher;
|
using ast_matchers::internal::Matcher;
|
||||||
|
@ -186,6 +201,18 @@ TEST(ParserTest, Errors) {
|
||||||
EXPECT_EQ("1:1: Error parsing argument 1 for matcher Foo.\n"
|
EXPECT_EQ("1:1: Error parsing argument 1 for matcher Foo.\n"
|
||||||
"1:5: Invalid token <(> found when looking for a value.",
|
"1:5: Invalid token <(> found when looking for a value.",
|
||||||
ParseWithError("Foo(("));
|
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
|
} // end anonymous namespace
|
||||||
|
|
Loading…
Reference in New Issue