[clang] 04b69d9 - Add clang-query support for mapAnyOf
Stephen Kelly via cfe-commits
cfe-commits at lists.llvm.org
Sun Feb 7 07:46:44 PST 2021
Author: Stephen Kelly
Date: 2021-02-07T15:40:15Z
New Revision: 04b69d9a6013cbc39321452fa09e24fed4c98bc7
URL: https://github.com/llvm/llvm-project/commit/04b69d9a6013cbc39321452fa09e24fed4c98bc7
DIFF: https://github.com/llvm/llvm-project/commit/04b69d9a6013cbc39321452fa09e24fed4c98bc7.diff
LOG: Add clang-query support for mapAnyOf
Differential Revision: https://reviews.llvm.org/D94880
Added:
Modified:
clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h
clang/include/clang/ASTMatchers/Dynamic/Parser.h
clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp
clang/lib/ASTMatchers/Dynamic/Parser.cpp
clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h b/clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h
index f095dcdd60b0..10625311c1a5 100644
--- a/clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h
+++ b/clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h
@@ -66,6 +66,8 @@ class Diagnostics {
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 @@ class Diagnostics {
ET_ParserMalformedBindExpr = 107,
ET_ParserTrailingCode = 108,
ET_ParserNumberError = 109,
- ET_ParserOverloadedType = 110
+ ET_ParserOverloadedType = 110,
+ ET_ParserMalformedChainedExpr = 111,
+ ET_ParserFailedToBuildMatcher = 112
};
/// Helper stream class.
diff --git a/clang/include/clang/ASTMatchers/Dynamic/Parser.h b/clang/include/clang/ASTMatchers/Dynamic/Parser.h
index 384e88ff5edf..af370d83782a 100644
--- a/clang/include/clang/ASTMatchers/Dynamic/Parser.h
+++ b/clang/include/clang/ASTMatchers/Dynamic/Parser.h
@@ -100,6 +100,14 @@ class Parser {
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 @@ class Parser {
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 @@ class Parser {
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,
diff --git a/clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp b/clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp
index 88c2279afb2e..ba2f49e6b623 100644
--- a/clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp
@@ -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>";
diff --git a/clang/lib/ASTMatchers/Dynamic/Parser.cpp b/clang/lib/ASTMatchers/Dynamic/Parser.cpp
index efe581284fb9..c6a77bb6c2e0 100644
--- a/clang/lib/ASTMatchers/Dynamic/Parser.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Parser.cpp
@@ -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 (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 (BindToken.Kind != TokenInfo::TK_Ident ||
- BindToken.Text != TokenInfo::ID_Bind) {
- Error->addError(BindToken.Range, Error->ET_ParserMalformedBindExpr);
+ 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) {
diff --git a/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp
index 6fc720ed982f..744be79d87ff 100644
--- a/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp
+++ b/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp
@@ -32,6 +32,17 @@ class MockSema : public Parser::Sema {
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());
}
{
More information about the cfe-commits
mailing list