Add clang-query support for mapAnyOf

Differential Revision: https://reviews.llvm.org/D94880
This commit is contained in:
Stephen Kelly 2021-02-06 23:39:48 +00:00
parent 816cc43281
commit 04b69d9a60
5 changed files with 273 additions and 15 deletions

View File

@ -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.

View File

@ -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,

View File

@ -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>";

View File

@ -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) {

View File

@ -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());
}
{