diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/ASTMatchers/Dynamic/Parser.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/ASTMatchers/Dynamic/Parser.cpp | 264 |
1 files changed, 210 insertions, 54 deletions
diff --git a/contrib/llvm/tools/clang/lib/ASTMatchers/Dynamic/Parser.cpp b/contrib/llvm/tools/clang/lib/ASTMatchers/Dynamic/Parser.cpp index df9596e..25629d9 100644 --- a/contrib/llvm/tools/clang/lib/ASTMatchers/Dynamic/Parser.cpp +++ b/contrib/llvm/tools/clang/lib/ASTMatchers/Dynamic/Parser.cpp @@ -12,13 +12,13 @@ /// //===----------------------------------------------------------------------===// -#include <string> -#include <vector> - #include "clang/ASTMatchers/Dynamic/Parser.h" #include "clang/ASTMatchers/Dynamic/Registry.h" #include "clang/Basic/CharInfo.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/Twine.h" +#include <string> +#include <vector> namespace clang { namespace ast_matchers { @@ -28,15 +28,16 @@ namespace dynamic { struct Parser::TokenInfo { /// \brief Different possible tokens. enum TokenKind { - TK_Eof = 0, - TK_OpenParen = 1, - TK_CloseParen = 2, - TK_Comma = 3, - TK_Period = 4, - TK_Literal = 5, - TK_Ident = 6, - TK_InvalidChar = 7, - TK_Error = 8 + TK_Eof, + TK_OpenParen, + TK_CloseParen, + TK_Comma, + TK_Period, + TK_Literal, + TK_Ident, + TK_InvalidChar, + TK_Error, + TK_CodeCompletion }; /// \brief Some known identifiers. @@ -56,7 +57,15 @@ const char* const Parser::TokenInfo::ID_Bind = "bind"; class Parser::CodeTokenizer { public: explicit CodeTokenizer(StringRef MatcherCode, Diagnostics *Error) - : Code(MatcherCode), StartOfLine(MatcherCode), Line(1), Error(Error) { + : Code(MatcherCode), StartOfLine(MatcherCode), Line(1), Error(Error), + CodeCompletionLocation(nullptr) { + NextToken = getNextToken(); + } + + CodeTokenizer(StringRef MatcherCode, Diagnostics *Error, + unsigned CodeCompletionOffset) + : Code(MatcherCode), StartOfLine(MatcherCode), Line(1), Error(Error), + CodeCompletionLocation(MatcherCode.data() + CodeCompletionOffset) { NextToken = getNextToken(); } @@ -78,6 +87,13 @@ private: TokenInfo Result; Result.Range.Start = currentLocation(); + if (CodeCompletionLocation && CodeCompletionLocation <= Code.data()) { + Result.Kind = TokenInfo::TK_CodeCompletion; + Result.Text = StringRef(CodeCompletionLocation, 0); + CodeCompletionLocation = nullptr; + return Result; + } + if (Code.empty()) { Result.Kind = TokenInfo::TK_Eof; Result.Text = ""; @@ -122,8 +138,21 @@ private: if (isAlphanumeric(Code[0])) { // Parse an identifier size_t TokenLength = 1; - while (TokenLength < Code.size() && isAlphanumeric(Code[TokenLength])) + while (1) { + // A code completion location in/immediately after an identifier will + // cause the portion of the identifier before the code completion + // location to become a code completion token. + if (CodeCompletionLocation == Code.data() + TokenLength) { + CodeCompletionLocation = nullptr; + Result.Kind = TokenInfo::TK_CodeCompletion; + Result.Text = Code.substr(0, TokenLength); + Code = Code.drop_front(TokenLength); + return Result; + } + if (TokenLength == Code.size() || !isAlphanumeric(Code[TokenLength])) + break; ++TokenLength; + } Result.Kind = TokenInfo::TK_Ident; Result.Text = Code.substr(0, TokenLength); Code = Code.drop_front(TokenLength); @@ -224,16 +253,68 @@ private: unsigned Line; Diagnostics *Error; TokenInfo NextToken; + const char *CodeCompletionLocation; }; Parser::Sema::~Sema() {} +VariantValue Parser::Sema::getNamedValue(StringRef Name) { + return VariantValue(); +} + +struct Parser::ScopedContextEntry { + Parser *P; + + ScopedContextEntry(Parser *P, MatcherCtor C) : P(P) { + P->ContextStack.push_back(std::make_pair(C, 0u)); + } + + ~ScopedContextEntry() { + P->ContextStack.pop_back(); + } + + void nextArg() { + ++P->ContextStack.back().second; + } +}; + +/// \brief Parse expressions that start with an identifier. +/// +/// This function can parse named values and matchers. +/// In case of failure it will try to determine the user's intent to give +/// an appropriate error message. +bool Parser::parseIdentifierPrefixImpl(VariantValue *Value) { + const TokenInfo NameToken = Tokenizer->consumeNextToken(); + + if (Tokenizer->nextTokenKind() != TokenInfo::TK_OpenParen) { + // Parse as a named value. + if (const VariantValue NamedValue = S->getNamedValue(NameToken.Text)) { + *Value = NamedValue; + return true; + } + // If the syntax is correct and the name is not a matcher either, report + // unknown named value. + if ((Tokenizer->nextTokenKind() == TokenInfo::TK_Comma || + Tokenizer->nextTokenKind() == TokenInfo::TK_CloseParen || + Tokenizer->nextTokenKind() == TokenInfo::TK_Eof) && + !S->lookupMatcherCtor(NameToken.Text)) { + Error->addError(NameToken.Range, Error->ET_RegistryValueNotFound) + << NameToken.Text; + return false; + } + // Otherwise, fallback to the matcher parser. + } + + // Parse as a matcher expression. + return parseMatcherExpressionImpl(NameToken, Value); +} + /// \brief 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(VariantValue *Value) { - const TokenInfo NameToken = Tokenizer->consumeNextToken(); +bool Parser::parseMatcherExpressionImpl(const TokenInfo &NameToken, + VariantValue *Value) { assert(NameToken.Kind == TokenInfo::TK_Ident); const TokenInfo OpenToken = Tokenizer->consumeNextToken(); if (OpenToken.Kind != TokenInfo::TK_OpenParen) { @@ -242,32 +323,49 @@ bool Parser::parseMatcherExpressionImpl(VariantValue *Value) { return false; } + llvm::Optional<MatcherCtor> 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. + } + std::vector<ParserValue> Args; TokenInfo EndToken; - while (Tokenizer->nextTokenKind() != TokenInfo::TK_Eof) { - if (Tokenizer->nextTokenKind() == TokenInfo::TK_CloseParen) { - // End of args. - EndToken = Tokenizer->consumeNextToken(); - break; - } - if (Args.size() > 0) { - // 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; + + { + ScopedContextEntry SCE(this, Ctor ? *Ctor : nullptr); + + while (Tokenizer->nextTokenKind() != TokenInfo::TK_Eof) { + if (Tokenizer->nextTokenKind() == TokenInfo::TK_CloseParen) { + // End of args. + EndToken = Tokenizer->consumeNextToken(); + break; + } + if (Args.size() > 0) { + // 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; - ArgValue.Text = Tokenizer->peekNextToken().Text; - ArgValue.Range = Tokenizer->peekNextToken().Range; - if (!parseExpressionImpl(&ArgValue.Value)) return false; + Diagnostics::Context Ctx(Diagnostics::Context::MatcherArg, Error, + NameToken.Text, NameToken.Range, + Args.size() + 1); + ParserValue ArgValue; + ArgValue.Text = Tokenizer->peekNextToken().Text; + ArgValue.Range = Tokenizer->peekNextToken().Range; + if (!parseExpressionImpl(&ArgValue.Value)) { + return false; + } - Args.push_back(ArgValue); + Args.push_back(ArgValue); + SCE.nextArg(); + } } if (EndToken.Kind == TokenInfo::TK_Eof) { @@ -280,6 +378,11 @@ bool Parser::parseMatcherExpressionImpl(VariantValue *Value) { // Parse .bind("foo") Tokenizer->consumeNextToken(); // consume the period. const TokenInfo BindToken = Tokenizer->consumeNextToken(); + if (BindToken.Kind == TokenInfo::TK_CodeCompletion) { + addCompletion(BindToken, "bind(\"", "bind"); + return false; + } + const TokenInfo OpenToken = Tokenizer->consumeNextToken(); const TokenInfo IDToken = Tokenizer->consumeNextToken(); const TokenInfo CloseToken = Tokenizer->consumeNextToken(); @@ -306,19 +409,55 @@ bool Parser::parseMatcherExpressionImpl(VariantValue *Value) { BindID = IDToken.Value.getString(); } + if (!Ctor) + return false; + // Merge the start and end infos. Diagnostics::Context Ctx(Diagnostics::Context::ConstructMatcher, Error, NameToken.Text, NameToken.Range); SourceRange MatcherRange = NameToken.Range; MatcherRange.End = EndToken.Range.End; VariantMatcher Result = S->actOnMatcherExpression( - NameToken.Text, MatcherRange, BindID, Args, Error); + *Ctor, MatcherRange, BindID, Args, Error); if (Result.isNull()) return false; *Value = Result; return true; } +// If the prefix of this completion matches the completion token, add it to +// Completions minus the prefix. +void Parser::addCompletion(const TokenInfo &CompToken, StringRef TypedText, + StringRef Decl) { + if (TypedText.size() >= CompToken.Text.size() && + TypedText.substr(0, CompToken.Text.size()) == CompToken.Text) { + Completions.push_back( + MatcherCompletion(TypedText.substr(CompToken.Text.size()), Decl)); + } +} + +void Parser::addExpressionCompletions() { + const TokenInfo CompToken = Tokenizer->consumeNextToken(); + assert(CompToken.Kind == TokenInfo::TK_CodeCompletion); + + // We cannot complete code if there is an invalid element on the context + // stack. + for (ContextStackTy::iterator I = ContextStack.begin(), + E = ContextStack.end(); + I != E; ++I) { + if (!I->first) + return; + } + + std::vector<MatcherCompletion> RegCompletions = + Registry::getCompletions(ContextStack); + for (std::vector<MatcherCompletion>::iterator I = RegCompletions.begin(), + E = RegCompletions.end(); + I != E; ++I) { + addCompletion(CompToken, I->TypedText, I->MatcherDecl); + } +} + /// \brief Parse an <Expresssion> bool Parser::parseExpressionImpl(VariantValue *Value) { switch (Tokenizer->nextTokenKind()) { @@ -327,7 +466,11 @@ bool Parser::parseExpressionImpl(VariantValue *Value) { return true; case TokenInfo::TK_Ident: - return parseMatcherExpressionImpl(Value); + return parseIdentifierPrefixImpl(Value); + + case TokenInfo::TK_CodeCompletion: + addExpressionCompletions(); + return false; case TokenInfo::TK_Eof: Error->addError(Tokenizer->consumeNextToken().Range, @@ -355,22 +498,23 @@ Parser::Parser(CodeTokenizer *Tokenizer, Sema *S, Diagnostics *Error) : Tokenizer(Tokenizer), S(S), Error(Error) {} -class RegistrySema : public Parser::Sema { -public: - virtual ~RegistrySema() {} - VariantMatcher actOnMatcherExpression(StringRef MatcherName, - const SourceRange &NameRange, - StringRef BindID, - ArrayRef<ParserValue> Args, - Diagnostics *Error) { - if (BindID.empty()) { - return Registry::constructMatcher(MatcherName, NameRange, Args, Error); - } else { - return Registry::constructBoundMatcher(MatcherName, NameRange, BindID, - Args, Error); - } +Parser::RegistrySema::~RegistrySema() {} + +llvm::Optional<MatcherCtor> +Parser::RegistrySema::lookupMatcherCtor(StringRef MatcherName) { + return Registry::lookupMatcherCtor(MatcherName); +} + +VariantMatcher Parser::RegistrySema::actOnMatcherExpression( + MatcherCtor Ctor, const SourceRange &NameRange, StringRef BindID, + ArrayRef<ParserValue> Args, Diagnostics *Error) { + if (BindID.empty()) { + return Registry::constructMatcher(Ctor, NameRange, Args, Error); + } else { + return Registry::constructBoundMatcher(Ctor, NameRange, BindID, Args, + Error); } -}; +} bool Parser::parseExpression(StringRef Code, VariantValue *Value, Diagnostics *Error) { @@ -390,6 +534,18 @@ bool Parser::parseExpression(StringRef Code, Sema *S, return true; } +std::vector<MatcherCompletion> +Parser::completeExpression(StringRef Code, unsigned CompletionOffset) { + Diagnostics Error; + CodeTokenizer Tokenizer(Code, &Error, CompletionOffset); + RegistrySema S; + Parser P(&Tokenizer, &S, &Error); + VariantValue Dummy; + P.parseExpressionImpl(&Dummy); + + return P.Completions; +} + llvm::Optional<DynTypedMatcher> Parser::parseMatcherExpression(StringRef Code, Diagnostics *Error) { RegistrySema S; |