r200497 - Introduce Parser::completeExpression.

Peter Collingbourne peter at pcc.me.uk
Thu Jan 30 14:38:42 PST 2014


Author: pcc
Date: Thu Jan 30 16:38:41 2014
New Revision: 200497

URL: http://llvm.org/viewvc/llvm-project?rev=200497&view=rev
Log:
Introduce Parser::completeExpression.

This function returns a list of completions for a given expression and
completion position.

Differential Revision: http://llvm-reviews.chandlerc.com/D2261

Modified:
    cfe/trunk/include/clang/ASTMatchers/Dynamic/Parser.h
    cfe/trunk/lib/ASTMatchers/Dynamic/Parser.cpp
    cfe/trunk/unittests/ASTMatchers/Dynamic/ParserTest.cpp

Modified: cfe/trunk/include/clang/ASTMatchers/Dynamic/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/ASTMatchers/Dynamic/Parser.h?rev=200497&r1=200496&r2=200497&view=diff
==============================================================================
--- cfe/trunk/include/clang/ASTMatchers/Dynamic/Parser.h (original)
+++ cfe/trunk/include/clang/ASTMatchers/Dynamic/Parser.h Thu Jan 30 16:38:41 2014
@@ -144,8 +144,16 @@ public:
   static bool parseExpression(StringRef Code, Sema *S,
                               VariantValue *Value, Diagnostics *Error);
 
+  /// \brief Complete an expression at the given offset.
+  ///
+  /// \return The list of completions, which may be empty if there are no
+  /// available completions or if an error occurred.
+  static std::vector<MatcherCompletion>
+  completeExpression(StringRef Code, unsigned CompletionOffset);
+
 private:
   class CodeTokenizer;
+  struct ScopedContextEntry;
   struct TokenInfo;
 
   Parser(CodeTokenizer *Tokenizer, Sema *S,
@@ -154,9 +162,17 @@ private:
   bool parseExpressionImpl(VariantValue *Value);
   bool parseMatcherExpressionImpl(VariantValue *Value);
 
+  void addCompletion(const TokenInfo &CompToken, StringRef TypedText,
+                     StringRef Decl);
+  void addExpressionCompletions();
+
   CodeTokenizer *const Tokenizer;
   Sema *const S;
   Diagnostics *const Error;
+
+  typedef std::vector<std::pair<MatcherCtor, unsigned> > ContextStackTy;
+  ContextStackTy ContextStack;
+  std::vector<MatcherCompletion> Completions;
 };
 
 }  // namespace dynamic

Modified: cfe/trunk/lib/ASTMatchers/Dynamic/Parser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/ASTMatchers/Dynamic/Parser.cpp?rev=200497&r1=200496&r2=200497&view=diff
==============================================================================
--- cfe/trunk/lib/ASTMatchers/Dynamic/Parser.cpp (original)
+++ cfe/trunk/lib/ASTMatchers/Dynamic/Parser.cpp Thu Jan 30 16:38:41 2014
@@ -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_
 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(0) {
+    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 = 0;
+      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 = 0;
+            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,10 +253,27 @@ private:
   unsigned Line;
   Diagnostics *Error;
   TokenInfo NextToken;
+  const char *CodeCompletionLocation;
 };
 
 Parser::Sema::~Sema() {}
 
+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 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
@@ -244,33 +290,41 @@ bool Parser::parseMatcherExpressionImpl(
 
   llvm::Optional<MatcherCtor> Ctor =
       S->lookupMatcherCtor(NameToken.Text, NameToken.Range, Error);
-
   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 : 0);
+
+    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) {
@@ -283,6 +337,11 @@ bool Parser::parseMatcherExpressionImpl(
     // 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();
@@ -325,6 +384,39 @@ bool Parser::parseMatcherExpressionImpl(
   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()) {
@@ -335,6 +427,10 @@ bool Parser::parseExpressionImpl(Variant
   case TokenInfo::TK_Ident:
     return parseMatcherExpressionImpl(Value);
 
+  case TokenInfo::TK_CodeCompletion:
+    addExpressionCompletions();
+    return false;
+
   case TokenInfo::TK_Eof:
     Error->addError(Tokenizer->consumeNextToken().Range,
                     Error->ET_ParserNoCode);
@@ -401,6 +497,18 @@ bool Parser::parseExpression(StringRef C
   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;

Modified: cfe/trunk/unittests/ASTMatchers/Dynamic/ParserTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/ASTMatchers/Dynamic/ParserTest.cpp?rev=200497&r1=200496&r2=200497&view=diff
==============================================================================
--- cfe/trunk/unittests/ASTMatchers/Dynamic/ParserTest.cpp (original)
+++ cfe/trunk/unittests/ASTMatchers/Dynamic/ParserTest.cpp Thu Jan 30 16:38:41 2014
@@ -245,6 +245,20 @@ TEST(ParserTest, OverloadErrors) {
             ParseWithError("callee(\"A\")"));
 }
 
+TEST(ParserTest, Completion) {
+  std::vector<MatcherCompletion> Comps =
+      Parser::completeExpression("while", 5);
+  ASSERT_EQ(1u, Comps.size());
+  EXPECT_EQ("Stmt(", Comps[0].TypedText);
+  EXPECT_EQ("Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)",
+            Comps[0].MatcherDecl);
+
+  Comps = Parser::completeExpression("whileStmt().", 12);
+  ASSERT_EQ(1u, Comps.size());
+  EXPECT_EQ("bind(\"", Comps[0].TypedText);
+  EXPECT_EQ("bind", Comps[0].MatcherDecl);
+}
+
 }  // end anonymous namespace
 }  // end namespace dynamic
 }  // end namespace ast_matchers





More information about the cfe-commits mailing list