forked from OSchip/llvm-project
Add clang-query support for mapAnyOf
Differential Revision: https://reviews.llvm.org/D94880
This commit is contained in:
parent
816cc43281
commit
04b69d9a60
|
@ -66,6 +66,8 @@ public:
|
|||
ET_RegistryAmbiguousOverload = 5,
|
||||
ET_RegistryValueNotFound = 6,
|
||||
ET_RegistryUnknownEnumWithReplace = 7,
|
||||
ET_RegistryNonNodeMatcher = 8,
|
||||
ET_RegistryMatcherNoWithSupport = 9,
|
||||
|
||||
ET_ParserStringError = 100,
|
||||
ET_ParserNoOpenParen = 101,
|
||||
|
@ -77,7 +79,9 @@ public:
|
|||
ET_ParserMalformedBindExpr = 107,
|
||||
ET_ParserTrailingCode = 108,
|
||||
ET_ParserNumberError = 109,
|
||||
ET_ParserOverloadedType = 110
|
||||
ET_ParserOverloadedType = 110,
|
||||
ET_ParserMalformedChainedExpr = 111,
|
||||
ET_ParserFailedToBuildMatcher = 112
|
||||
};
|
||||
|
||||
/// Helper stream class.
|
||||
|
|
|
@ -100,6 +100,14 @@ public:
|
|||
virtual llvm::Optional<MatcherCtor>
|
||||
lookupMatcherCtor(StringRef MatcherName) = 0;
|
||||
|
||||
virtual bool isBuilderMatcher(MatcherCtor) const = 0;
|
||||
|
||||
virtual ASTNodeKind nodeMatcherType(MatcherCtor) const = 0;
|
||||
|
||||
virtual internal::MatcherDescriptorPtr
|
||||
buildMatcherCtor(MatcherCtor, SourceRange NameRange,
|
||||
ArrayRef<ParserValue> Args, Diagnostics *Error) const = 0;
|
||||
|
||||
/// Compute the list of completion types for \p Context.
|
||||
///
|
||||
/// Each element of \p Context represents a matcher invocation, going from
|
||||
|
@ -142,6 +150,15 @@ public:
|
|||
std::vector<ArgKind> getAcceptedCompletionTypes(
|
||||
llvm::ArrayRef<std::pair<MatcherCtor, unsigned>> Context) override;
|
||||
|
||||
bool isBuilderMatcher(MatcherCtor Ctor) const override;
|
||||
|
||||
ASTNodeKind nodeMatcherType(MatcherCtor) const override;
|
||||
|
||||
internal::MatcherDescriptorPtr
|
||||
buildMatcherCtor(MatcherCtor, SourceRange NameRange,
|
||||
ArrayRef<ParserValue> Args,
|
||||
Diagnostics *Error) const override;
|
||||
|
||||
std::vector<MatcherCompletion>
|
||||
getMatcherCompletions(llvm::ArrayRef<ArgKind> AcceptedTypes) override;
|
||||
};
|
||||
|
@ -233,6 +250,8 @@ private:
|
|||
|
||||
bool parseBindID(std::string &BindID);
|
||||
bool parseExpressionImpl(VariantValue *Value);
|
||||
bool parseMatcherBuilder(MatcherCtor Ctor, const TokenInfo &NameToken,
|
||||
const TokenInfo &OpenToken, VariantValue *Value);
|
||||
bool parseMatcherExpressionImpl(const TokenInfo &NameToken,
|
||||
const TokenInfo &OpenToken,
|
||||
llvm::Optional<MatcherCtor> Ctor,
|
||||
|
|
|
@ -100,6 +100,10 @@ static StringRef errorTypeToFormatString(Diagnostics::ErrorType Type) {
|
|||
return "Value not found: $0";
|
||||
case Diagnostics::ET_RegistryUnknownEnumWithReplace:
|
||||
return "Unknown value '$1' for arg $0; did you mean '$2'";
|
||||
case Diagnostics::ET_RegistryNonNodeMatcher:
|
||||
return "Matcher not a node matcher: $0";
|
||||
case Diagnostics::ET_RegistryMatcherNoWithSupport:
|
||||
return "Matcher does not support with call.";
|
||||
|
||||
case Diagnostics::ET_ParserStringError:
|
||||
return "Error parsing string token: <$0>";
|
||||
|
@ -123,6 +127,10 @@ static StringRef errorTypeToFormatString(Diagnostics::ErrorType Type) {
|
|||
return "Error parsing numeric literal: <$0>";
|
||||
case Diagnostics::ET_ParserOverloadedType:
|
||||
return "Input value has unresolved overloaded type: $0";
|
||||
case Diagnostics::ET_ParserMalformedChainedExpr:
|
||||
return "Period not followed by valid chained call.";
|
||||
case Diagnostics::ET_ParserFailedToBuildMatcher:
|
||||
return "Failed to build matcher: $0.";
|
||||
|
||||
case Diagnostics::ET_None:
|
||||
return "<N/A>";
|
||||
|
|
|
@ -52,6 +52,7 @@ struct Parser::TokenInfo {
|
|||
|
||||
/// Some known identifiers.
|
||||
static const char* const ID_Bind;
|
||||
static const char *const ID_With;
|
||||
|
||||
TokenInfo() = default;
|
||||
|
||||
|
@ -62,6 +63,7 @@ struct Parser::TokenInfo {
|
|||
};
|
||||
|
||||
const char* const Parser::TokenInfo::ID_Bind = "bind";
|
||||
const char *const Parser::TokenInfo::ID_With = "with";
|
||||
|
||||
/// Simple tokenizer for the parser.
|
||||
class Parser::CodeTokenizer {
|
||||
|
@ -367,14 +369,26 @@ bool Parser::parseIdentifierPrefixImpl(VariantValue *Value) {
|
|||
|
||||
std::string BindID;
|
||||
Tokenizer->consumeNextToken();
|
||||
TokenInfo BindToken = Tokenizer->consumeNextToken();
|
||||
if (BindToken.Kind == TokenInfo::TK_CodeCompletion) {
|
||||
addCompletion(BindToken, MatcherCompletion("bind(\"", "bind", 1));
|
||||
TokenInfo ChainCallToken = Tokenizer->consumeNextToken();
|
||||
if (ChainCallToken.Kind == TokenInfo::TK_CodeCompletion) {
|
||||
addCompletion(ChainCallToken, MatcherCompletion("bind(\"", "bind", 1));
|
||||
return false;
|
||||
}
|
||||
if (BindToken.Kind != TokenInfo::TK_Ident ||
|
||||
BindToken.Text != TokenInfo::ID_Bind) {
|
||||
Error->addError(BindToken.Range, Error->ET_ParserMalformedBindExpr);
|
||||
|
||||
if (ChainCallToken.Kind != TokenInfo::TK_Ident ||
|
||||
(ChainCallToken.Text != TokenInfo::ID_Bind &&
|
||||
ChainCallToken.Text != TokenInfo::ID_With)) {
|
||||
Error->addError(ChainCallToken.Range,
|
||||
Error->ET_ParserMalformedChainedExpr);
|
||||
return false;
|
||||
}
|
||||
if (ChainCallToken.Text == TokenInfo::ID_With) {
|
||||
|
||||
Diagnostics::Context Ctx(Diagnostics::Context::ConstructMatcher, Error,
|
||||
NameToken.Text, NameToken.Range);
|
||||
|
||||
Error->addError(ChainCallToken.Range,
|
||||
Error->ET_RegistryMatcherNoWithSupport);
|
||||
return false;
|
||||
}
|
||||
if (!parseBindID(BindID))
|
||||
|
@ -454,6 +468,160 @@ bool Parser::parseBindID(std::string &BindID) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Parser::parseMatcherBuilder(MatcherCtor Ctor, const TokenInfo &NameToken,
|
||||
const TokenInfo &OpenToken,
|
||||
VariantValue *Value) {
|
||||
std::vector<ParserValue> Args;
|
||||
TokenInfo EndToken;
|
||||
|
||||
Tokenizer->SkipNewlines();
|
||||
|
||||
{
|
||||
ScopedContextEntry SCE(this, Ctor);
|
||||
|
||||
while (Tokenizer->nextTokenKind() != TokenInfo::TK_Eof) {
|
||||
if (Tokenizer->nextTokenKind() == TokenInfo::TK_CloseParen) {
|
||||
// End of args.
|
||||
EndToken = Tokenizer->consumeNextToken();
|
||||
break;
|
||||
}
|
||||
if (!Args.empty()) {
|
||||
// We must find a , token to continue.
|
||||
TokenInfo CommaToken = Tokenizer->consumeNextToken();
|
||||
if (CommaToken.Kind != TokenInfo::TK_Comma) {
|
||||
Error->addError(CommaToken.Range, Error->ET_ParserNoComma)
|
||||
<< CommaToken.Text;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Diagnostics::Context Ctx(Diagnostics::Context::MatcherArg, Error,
|
||||
NameToken.Text, NameToken.Range,
|
||||
Args.size() + 1);
|
||||
ParserValue ArgValue;
|
||||
Tokenizer->SkipNewlines();
|
||||
|
||||
if (Tokenizer->peekNextToken().Kind == TokenInfo::TK_CodeCompletion) {
|
||||
addExpressionCompletions();
|
||||
return false;
|
||||
}
|
||||
|
||||
TokenInfo NodeMatcherToken = Tokenizer->consumeNextToken();
|
||||
|
||||
if (NodeMatcherToken.Kind != TokenInfo::TK_Ident) {
|
||||
Error->addError(NameToken.Range, Error->ET_ParserFailedToBuildMatcher)
|
||||
<< NameToken.Text;
|
||||
return false;
|
||||
}
|
||||
|
||||
ArgValue.Text = NodeMatcherToken.Text;
|
||||
ArgValue.Range = NodeMatcherToken.Range;
|
||||
|
||||
llvm::Optional<MatcherCtor> MappedMatcher =
|
||||
S->lookupMatcherCtor(ArgValue.Text);
|
||||
|
||||
if (!MappedMatcher) {
|
||||
Error->addError(NodeMatcherToken.Range,
|
||||
Error->ET_RegistryMatcherNotFound)
|
||||
<< NodeMatcherToken.Text;
|
||||
return false;
|
||||
}
|
||||
|
||||
ASTNodeKind NK = S->nodeMatcherType(*MappedMatcher);
|
||||
|
||||
if (NK.isNone()) {
|
||||
Error->addError(NodeMatcherToken.Range,
|
||||
Error->ET_RegistryNonNodeMatcher)
|
||||
<< NodeMatcherToken.Text;
|
||||
return false;
|
||||
}
|
||||
|
||||
ArgValue.Value = NK;
|
||||
|
||||
Tokenizer->SkipNewlines();
|
||||
Args.push_back(ArgValue);
|
||||
|
||||
SCE.nextArg();
|
||||
}
|
||||
}
|
||||
|
||||
if (EndToken.Kind == TokenInfo::TK_Eof) {
|
||||
Error->addError(OpenToken.Range, Error->ET_ParserNoCloseParen);
|
||||
return false;
|
||||
}
|
||||
|
||||
internal::MatcherDescriptorPtr BuiltCtor =
|
||||
S->buildMatcherCtor(Ctor, NameToken.Range, Args, Error);
|
||||
|
||||
if (!BuiltCtor.get()) {
|
||||
Error->addError(NameToken.Range, Error->ET_ParserFailedToBuildMatcher)
|
||||
<< NameToken.Text;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string BindID;
|
||||
if (Tokenizer->peekNextToken().Kind == TokenInfo::TK_Period) {
|
||||
Tokenizer->consumeNextToken();
|
||||
TokenInfo ChainCallToken = Tokenizer->consumeNextToken();
|
||||
if (ChainCallToken.Kind == TokenInfo::TK_CodeCompletion) {
|
||||
addCompletion(ChainCallToken, MatcherCompletion("bind(\"", "bind", 1));
|
||||
addCompletion(ChainCallToken, MatcherCompletion("with(", "with", 1));
|
||||
return false;
|
||||
}
|
||||
if (ChainCallToken.Kind != TokenInfo::TK_Ident ||
|
||||
(ChainCallToken.Text != TokenInfo::ID_Bind &&
|
||||
ChainCallToken.Text != TokenInfo::ID_With)) {
|
||||
Error->addError(ChainCallToken.Range,
|
||||
Error->ET_ParserMalformedChainedExpr);
|
||||
return false;
|
||||
}
|
||||
if (ChainCallToken.Text == TokenInfo::ID_Bind) {
|
||||
if (!parseBindID(BindID))
|
||||
return false;
|
||||
Diagnostics::Context Ctx(Diagnostics::Context::ConstructMatcher, Error,
|
||||
NameToken.Text, NameToken.Range);
|
||||
SourceRange MatcherRange = NameToken.Range;
|
||||
MatcherRange.End = ChainCallToken.Range.End;
|
||||
VariantMatcher Result = S->actOnMatcherExpression(
|
||||
BuiltCtor.get(), MatcherRange, BindID, {}, Error);
|
||||
if (Result.isNull())
|
||||
return false;
|
||||
|
||||
*Value = Result;
|
||||
return true;
|
||||
} else if (ChainCallToken.Text == TokenInfo::ID_With) {
|
||||
Tokenizer->SkipNewlines();
|
||||
|
||||
if (Tokenizer->nextTokenKind() != TokenInfo::TK_OpenParen) {
|
||||
StringRef ErrTxt = Tokenizer->nextTokenKind() == TokenInfo::TK_Eof
|
||||
? StringRef("EOF")
|
||||
: Tokenizer->peekNextToken().Text;
|
||||
Error->addError(Tokenizer->peekNextToken().Range,
|
||||
Error->ET_ParserNoOpenParen)
|
||||
<< ErrTxt;
|
||||
return false;
|
||||
}
|
||||
|
||||
TokenInfo WithOpenToken = Tokenizer->consumeNextToken();
|
||||
|
||||
return parseMatcherExpressionImpl(NameToken, WithOpenToken,
|
||||
BuiltCtor.get(), Value);
|
||||
}
|
||||
}
|
||||
|
||||
Diagnostics::Context Ctx(Diagnostics::Context::ConstructMatcher, Error,
|
||||
NameToken.Text, NameToken.Range);
|
||||
SourceRange MatcherRange = NameToken.Range;
|
||||
MatcherRange.End = EndToken.Range.End;
|
||||
VariantMatcher Result = S->actOnMatcherExpression(
|
||||
BuiltCtor.get(), MatcherRange, BindID, {}, Error);
|
||||
if (Result.isNull())
|
||||
return false;
|
||||
|
||||
*Value = Result;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Parse and validate a matcher expression.
|
||||
/// \return \c true on success, in which case \c Value has the matcher parsed.
|
||||
/// If the input is malformed, or some argument has an error, it
|
||||
|
@ -468,6 +636,9 @@ bool Parser::parseMatcherExpressionImpl(const TokenInfo &NameToken,
|
|||
// Do not return here. We need to continue to give completion suggestions.
|
||||
}
|
||||
|
||||
if (Ctor && *Ctor && S->isBuilderMatcher(*Ctor))
|
||||
return parseMatcherBuilder(*Ctor, NameToken, OpenToken, Value);
|
||||
|
||||
std::vector<ParserValue> Args;
|
||||
TokenInfo EndToken;
|
||||
|
||||
|
@ -517,14 +688,29 @@ bool Parser::parseMatcherExpressionImpl(const TokenInfo &NameToken,
|
|||
std::string BindID;
|
||||
if (Tokenizer->peekNextToken().Kind == TokenInfo::TK_Period) {
|
||||
Tokenizer->consumeNextToken();
|
||||
TokenInfo BindToken = Tokenizer->consumeNextToken();
|
||||
if (BindToken.Kind == TokenInfo::TK_CodeCompletion) {
|
||||
addCompletion(BindToken, MatcherCompletion("bind(\"", "bind", 1));
|
||||
TokenInfo ChainCallToken = Tokenizer->consumeNextToken();
|
||||
if (ChainCallToken.Kind == TokenInfo::TK_CodeCompletion) {
|
||||
addCompletion(ChainCallToken, MatcherCompletion("bind(\"", "bind", 1));
|
||||
return false;
|
||||
}
|
||||
if (BindToken.Kind != TokenInfo::TK_Ident ||
|
||||
BindToken.Text != TokenInfo::ID_Bind) {
|
||||
Error->addError(BindToken.Range, Error->ET_ParserMalformedBindExpr);
|
||||
|
||||
if (ChainCallToken.Kind != TokenInfo::TK_Ident) {
|
||||
Error->addError(ChainCallToken.Range,
|
||||
Error->ET_ParserMalformedChainedExpr);
|
||||
return false;
|
||||
}
|
||||
if (ChainCallToken.Text == TokenInfo::ID_With) {
|
||||
|
||||
Diagnostics::Context Ctx(Diagnostics::Context::ConstructMatcher, Error,
|
||||
NameToken.Text, NameToken.Range);
|
||||
|
||||
Error->addError(ChainCallToken.Range,
|
||||
Error->ET_RegistryMatcherNoWithSupport);
|
||||
return false;
|
||||
}
|
||||
if (ChainCallToken.Text != TokenInfo::ID_Bind) {
|
||||
Error->addError(ChainCallToken.Range,
|
||||
Error->ET_ParserMalformedChainedExpr);
|
||||
return false;
|
||||
}
|
||||
if (!parseBindID(BindID))
|
||||
|
@ -668,6 +854,21 @@ std::vector<MatcherCompletion> Parser::RegistrySema::getMatcherCompletions(
|
|||
return Registry::getMatcherCompletions(AcceptedTypes);
|
||||
}
|
||||
|
||||
bool Parser::RegistrySema::isBuilderMatcher(MatcherCtor Ctor) const {
|
||||
return Registry::isBuilderMatcher(Ctor);
|
||||
}
|
||||
|
||||
ASTNodeKind Parser::RegistrySema::nodeMatcherType(MatcherCtor Ctor) const {
|
||||
return Registry::nodeMatcherType(Ctor);
|
||||
}
|
||||
|
||||
internal::MatcherDescriptorPtr
|
||||
Parser::RegistrySema::buildMatcherCtor(MatcherCtor Ctor, SourceRange NameRange,
|
||||
ArrayRef<ParserValue> Args,
|
||||
Diagnostics *Error) const {
|
||||
return Registry::buildMatcherCtor(Ctor, NameRange, Args, Error);
|
||||
}
|
||||
|
||||
bool Parser::parseExpression(StringRef &Code, Sema *S,
|
||||
const NamedValueMap *NamedValues,
|
||||
VariantValue *Value, Diagnostics *Error) {
|
||||
|
|
|
@ -32,6 +32,17 @@ public:
|
|||
return M.getID().second;
|
||||
}
|
||||
|
||||
bool isBuilderMatcher(MatcherCtor) const override { return false; }
|
||||
|
||||
ASTNodeKind nodeMatcherType(MatcherCtor) const override { return {}; }
|
||||
|
||||
internal::MatcherDescriptorPtr
|
||||
buildMatcherCtor(MatcherCtor, SourceRange NameRange,
|
||||
ArrayRef<ParserValue> Args,
|
||||
Diagnostics *Error) const override {
|
||||
return internal::MatcherDescriptorPtr{nullptr};
|
||||
}
|
||||
|
||||
void parse(StringRef Code) {
|
||||
Diagnostics Error;
|
||||
VariantValue Value;
|
||||
|
@ -329,7 +340,7 @@ TEST(ParserTest, Errors) {
|
|||
"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.",
|
||||
EXPECT_EQ("1:11: Period not followed by valid chained call.",
|
||||
ParseWithError("isArrow().biind"));
|
||||
EXPECT_EQ("1:15: Malformed bind() expression.",
|
||||
ParseWithError("isArrow().bind"));
|
||||
|
@ -340,6 +351,20 @@ TEST(ParserTest, Errors) {
|
|||
EXPECT_EQ("1:1: Error building matcher isArrow.\n"
|
||||
"1:1: Matcher does not support binding.",
|
||||
ParseWithError("isArrow().bind(\"foo\")"));
|
||||
EXPECT_EQ("1:1: Error building matcher isArrow.\n"
|
||||
"1:11: Matcher does not support with call.",
|
||||
ParseWithError("isArrow().with"));
|
||||
EXPECT_EQ(
|
||||
"1:22: Error parsing matcher. Found token <EOF> while looking for '('.",
|
||||
ParseWithError("mapAnyOf(ifStmt).with"));
|
||||
EXPECT_EQ(
|
||||
"1:22: Error parsing matcher. Found end-of-code while looking for ')'.",
|
||||
ParseWithError("mapAnyOf(ifStmt).with("));
|
||||
EXPECT_EQ("1:1: Failed to build matcher: mapAnyOf.",
|
||||
ParseWithError("mapAnyOf()"));
|
||||
EXPECT_EQ("1:1: Error parsing argument 1 for matcher mapAnyOf.\n1:1: Failed "
|
||||
"to build matcher: mapAnyOf.",
|
||||
ParseWithError("mapAnyOf(\"foo\")"));
|
||||
EXPECT_EQ("Input value has unresolved overloaded type: "
|
||||
"Matcher<DoStmt|ForStmt|WhileStmt|CXXForRangeStmt|FunctionDecl>",
|
||||
ParseMatcherWithError("hasBody(stmt())"));
|
||||
|
@ -470,7 +495,8 @@ decl()))matcher";
|
|||
)matcher";
|
||||
M = Parser::parseMatcherExpression(Code, nullptr, &NamedValues, &Error);
|
||||
EXPECT_FALSE(M.hasValue());
|
||||
EXPECT_EQ("1:11: Malformed bind() expression.", Error.toStringFull());
|
||||
EXPECT_EQ("1:11: Period not followed by valid chained call.",
|
||||
Error.toStringFull());
|
||||
}
|
||||
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue