diff --git a/clang/include/clang/ASTMatchers/Dynamic/Parser.h b/clang/include/clang/ASTMatchers/Dynamic/Parser.h --- a/clang/include/clang/ASTMatchers/Dynamic/Parser.h +++ b/clang/include/clang/ASTMatchers/Dynamic/Parser.h @@ -231,9 +231,13 @@ const NamedValueMap *NamedValues, Diagnostics *Error); - bool parseBindID(std::string &BindID); + bool parseBindID(TokenInfo BindToken, 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 Ctor, VariantValue *Value); bool parseIdentifierPrefixImpl(VariantValue *Value); diff --git a/clang/lib/ASTMatchers/Dynamic/Marshallers.h b/clang/lib/ASTMatchers/Dynamic/Marshallers.h --- a/clang/lib/ASTMatchers/Dynamic/Marshallers.h +++ b/clang/lib/ASTMatchers/Dynamic/Marshallers.h @@ -309,6 +309,14 @@ ArrayRef Args, Diagnostics *Error) const = 0; + virtual ASTNodeKind nodeMatcherType() const = 0; + + virtual bool isBuilderMatcher() const = 0; + + virtual std::unique_ptr + buildMatcherCtor(SourceRange NameRange, ArrayRef Args, + Diagnostics *Error) const = 0; + /// Returns whether the matcher is variadic. Variadic matchers can take any /// number of arguments, but they must be of the same type. virtual bool isVariadic() const = 0; @@ -386,6 +394,16 @@ return Marshaller(Func, MatcherName, NameRange, Args, Error); } + bool isBuilderMatcher() const override { return false; } + + std::unique_ptr + buildMatcherCtor(SourceRange NameRange, ArrayRef Args, + Diagnostics *Error) const override { + return {}; + } + + ASTNodeKind nodeMatcherType() const override { return ASTNodeKind(); } + bool isVariadic() const override { return false; } unsigned getNumArgs() const override { return ArgKinds.size(); } @@ -551,7 +569,8 @@ StringRef MatcherName) : Func(&variadicMatcherDescriptor), MatcherName(MatcherName.str()), - ArgsKind(ArgTypeTraits::getKind()) { + ArgsKind(ArgTypeTraits::getKind()), + ArgsNodeKind(ASTNodeKind::getFromNodeKind()) { BuildReturnTypeVector::build(RetKinds); } @@ -561,6 +580,16 @@ return Func(MatcherName, NameRange, Args, Error); } + bool isBuilderMatcher() const override { return false; } + + std::unique_ptr + buildMatcherCtor(SourceRange NameRange, ArrayRef Args, + Diagnostics *Error) const override { + return {}; + } + + ASTNodeKind nodeMatcherType() const override { return ArgsNodeKind; } + bool isVariadic() const override { return true; } unsigned getNumArgs() const override { return 0; } @@ -580,6 +609,7 @@ const std::string MatcherName; std::vector RetKinds; const ArgKind ArgsKind; + const ASTNodeKind ArgsNodeKind; }; /// Return CK_Trivial when appropriate for VariadicDynCastAllOfMatchers. @@ -610,6 +640,8 @@ } } + ASTNodeKind nodeMatcherType() const override { return DerivedKind; } + private: const ASTNodeKind DerivedKind; }; @@ -786,6 +818,15 @@ return false; } + bool isBuilderMatcher() const override { return false; } + std::unique_ptr + buildMatcherCtor(SourceRange NameRange, ArrayRef Args, + Diagnostics *Error) const override { + return {}; + } + + ASTNodeKind nodeMatcherType() const override { return ASTNodeKind(); } + private: std::vector> Overloads; }; @@ -856,6 +897,15 @@ ArgTypeTraits::get(Args[1].Value))); } + bool isBuilderMatcher() const override { return false; } + std::unique_ptr + buildMatcherCtor(SourceRange NameRange, ArrayRef Args, + Diagnostics *Error) const override { + return {}; + } + + ASTNodeKind nodeMatcherType() const override { return ASTNodeKind(); } + private: ReturnType (*const WithFlags)(StringRef, llvm::Regex::RegexFlags); ReturnType (*const NoFlags)(StringRef); @@ -918,6 +968,15 @@ bool isPolymorphic() const override { return true; } + bool isBuilderMatcher() const override { return false; } + std::unique_ptr + buildMatcherCtor(SourceRange NameRange, ArrayRef Args, + Diagnostics *Error) const override { + return {}; + } + + ASTNodeKind nodeMatcherType() const override { return ASTNodeKind(); } + private: const unsigned MinCount; const unsigned MaxCount; @@ -987,6 +1046,15 @@ *LeastDerivedKind = CladeNodeKind; return true; } + + bool isBuilderMatcher() const override { return false; } + std::unique_ptr + buildMatcherCtor(SourceRange NameRange, ArrayRef Args, + Diagnostics *Error) const override { + return {}; + } + + ASTNodeKind nodeMatcherType() const override { return ASTNodeKind(); } }; /// Helper functions to select the appropriate marshaller functions. diff --git a/clang/lib/ASTMatchers/Dynamic/Parser.cpp b/clang/lib/ASTMatchers/Dynamic/Parser.cpp --- a/clang/lib/ASTMatchers/Dynamic/Parser.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Parser.cpp @@ -20,6 +20,9 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ManagedStatic.h" + +#include "Marshallers.h" + #include #include #include @@ -52,6 +55,7 @@ /// Some known identifiers. static const char* const ID_Bind; + static const char *const ID_With; TokenInfo() = default; @@ -62,6 +66,7 @@ }; const char* const Parser::TokenInfo::ID_Bind = "bind"; +const char *const Parser::TokenInfo::ID_With = "with"; /// Simple tokenizer for the parser. class Parser::CodeTokenizer { @@ -366,7 +371,14 @@ } std::string BindID; - if (!parseBindID(BindID)) + Tokenizer->consumeNextToken(); // consume the period. + const 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 (!parseBindID(ChainCallToken, BindID)) return false; assert(NamedValue.isMatcher()); @@ -405,31 +417,26 @@ Tokenizer->SkipNewlines(); + assert(NameToken.Kind == TokenInfo::TK_Ident); + const TokenInfo OpenToken = Tokenizer->consumeNextToken(); + if (OpenToken.Kind != TokenInfo::TK_OpenParen) { + Error->addError(OpenToken.Range, Error->ET_ParserNoOpenParen) + << OpenToken.Text; + return false; + } + + llvm::Optional Ctor = S->lookupMatcherCtor(NameToken.Text); + // Parse as a matcher expression. - return parseMatcherExpressionImpl(NameToken, Value); + return parseMatcherExpressionImpl(NameToken, OpenToken, Ctor, Value); } -bool Parser::parseBindID(std::string &BindID) { +bool Parser::parseBindID(TokenInfo BindToken, std::string &BindID) { // Parse .bind("foo") - assert(Tokenizer->peekNextToken().Kind == TokenInfo::TK_Period); - Tokenizer->consumeNextToken(); // consume the period. - const TokenInfo BindToken = Tokenizer->consumeNextToken(); - if (BindToken.Kind == TokenInfo::TK_CodeCompletion) { - addCompletion(BindToken, MatcherCompletion("bind(\"", "bind", 1)); - return false; - } - const TokenInfo OpenToken = Tokenizer->consumeNextToken(); const TokenInfo IDToken = Tokenizer->consumeNextTokenIgnoreNewlines(); const TokenInfo CloseToken = Tokenizer->consumeNextTokenIgnoreNewlines(); - // 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->addError(BindToken.Range, Error->ET_ParserMalformedBindExpr); - return false; - } if (OpenToken.Kind != TokenInfo::TK_OpenParen) { Error->addError(OpenToken.Range, Error->ET_ParserMalformedBindExpr); return false; @@ -446,28 +453,140 @@ return true; } +bool Parser::parseMatcherBuilder(MatcherCtor Ctor, const TokenInfo &NameToken, + const TokenInfo &OpenToken, + VariantValue *Value) { + std::vector 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. + const 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(); + const auto NodeMatcherToken = Tokenizer->consumeNextToken(); + ArgValue.Text = NodeMatcherToken.Text; + ArgValue.Range = NodeMatcherToken.Range; + + auto MappedMatcher = S->lookupMatcherCtor(ArgValue.Text); + + if (!MappedMatcher) { + // TODO: error + } + + if ((*MappedMatcher)->nodeMatcherType().isNone()) { + // TODO: error + } + + ArgValue.Value = (*MappedMatcher)->nodeMatcherType(); + + Tokenizer->SkipNewlines(); + Args.push_back(ArgValue); + + SCE.nextArg(); + } + } + + if (EndToken.Kind == TokenInfo::TK_Eof) { + Error->addError(OpenToken.Range, Error->ET_ParserNoCloseParen); + return false; + } + + auto BuiltCtor = Ctor->buildMatcherCtor(NameToken.Range, Args, Error); + + if (!BuiltCtor) { + // Too early for completion? + return false; + } + + std::string BindID; + if (Tokenizer->peekNextToken().Kind == TokenInfo::TK_Period) { + Tokenizer->consumeNextToken(); // consume the period. + const 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) { + // TODO: Change error diagnostic to also account for with + Error->addError(ChainCallToken.Range, Error->ET_ParserMalformedBindExpr); + return false; + } + if (ChainCallToken.Text == TokenInfo::ID_Bind) { + if (!parseBindID(ChainCallToken, 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(); + const 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 /// returns \c false. bool Parser::parseMatcherExpressionImpl(const TokenInfo &NameToken, + const TokenInfo &OpenToken, + llvm::Optional Ctor, VariantValue *Value) { - assert(NameToken.Kind == TokenInfo::TK_Ident); - const TokenInfo OpenToken = Tokenizer->consumeNextToken(); - if (OpenToken.Kind != TokenInfo::TK_OpenParen) { - Error->addError(OpenToken.Range, Error->ET_ParserNoOpenParen) - << OpenToken.Text; - return false; - } - - llvm::Optional Ctor = S->lookupMatcherCtor(NameToken.Text); - if (!Ctor) { Error->addError(NameToken.Range, Error->ET_RegistryMatcherNotFound) << NameToken.Text; // Do not return here. We need to continue to give completion suggestions. } + if (Ctor && *Ctor && (*Ctor)->isBuilderMatcher()) { + return parseMatcherBuilder(*Ctor, NameToken, OpenToken, Value); + } + std::vector Args; TokenInfo EndToken; @@ -516,7 +635,15 @@ std::string BindID; if (Tokenizer->peekNextToken().Kind == TokenInfo::TK_Period) { - if (!parseBindID(BindID)) + Tokenizer->consumeNextToken(); + const 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 (!parseBindID(ChainCallToken, BindID)) return false; }