summaryrefslogtreecommitdiffstats
path: root/contrib/llvm/tools/clang/lib/ASTMatchers/Dynamic/Parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm/tools/clang/lib/ASTMatchers/Dynamic/Parser.cpp')
-rw-r--r--contrib/llvm/tools/clang/lib/ASTMatchers/Dynamic/Parser.cpp264
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;
OpenPOWER on IntegriCloud