Add support for .bind("foo") expressions on the dynamic matchers.

Summary: Add support on the parser, registry, and DynTypedMatcher for binding IDs dynamically.

Reviewers: klimek

CC: cfe-commits, revane

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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@183144 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/ASTMatchers/Dynamic/Parser.cpp b/lib/ASTMatchers/Dynamic/Parser.cpp
index 1678820..1ed40f3 100644
--- a/lib/ASTMatchers/Dynamic/Parser.cpp
+++ b/lib/ASTMatchers/Dynamic/Parser.cpp
@@ -32,12 +32,16 @@
     TK_OpenParen = 1,
     TK_CloseParen = 2,
     TK_Comma = 3,
-    TK_Literal = 4,
-    TK_Ident = 5,
-    TK_InvalidChar = 6,
-    TK_Error = 7
+    TK_Period = 4,
+    TK_Literal = 5,
+    TK_Ident = 6,
+    TK_InvalidChar = 7,
+    TK_Error = 8
   };
 
+  /// \brief Some known identifiers.
+  static const char* const ID_Bind;
+
   TokenInfo() : Text(), Kind(TK_Eof), Range(), Value() {}
 
   StringRef Text;
@@ -46,6 +50,8 @@
   VariantValue Value;
 };
 
+const char* const Parser::TokenInfo::ID_Bind = "bind";
+
 /// \brief Simple tokenizer for the parser.
 class Parser::CodeTokenizer {
 public:
@@ -84,6 +90,11 @@
       Result.Text = Code.substr(0, 1);
       Code = Code.drop_front();
       break;
+    case '.':
+      Result.Kind = TokenInfo::TK_Period;
+      Result.Text = Code.substr(0, 1);
+      Code = Code.drop_front();
+      break;
     case '(':
       Result.Kind = TokenInfo::TK_OpenParen;
       Result.Text = Code.substr(0, 1);
@@ -234,11 +245,43 @@
     return false;
   }
 
+  std::string BindID;
+  if (Tokenizer->peekNextToken().Kind == TokenInfo::TK_Period) {
+    // Parse .bind("foo")
+    Tokenizer->consumeNextToken();  // consume the period.
+    const TokenInfo BindToken = Tokenizer->consumeNextToken();
+    const TokenInfo OpenToken = Tokenizer->consumeNextToken();
+    const TokenInfo IDToken = Tokenizer->consumeNextToken();
+    const TokenInfo CloseToken = Tokenizer->consumeNextToken();
+
+    // 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->pushErrorFrame(BindToken.Range, Error->ET_ParserMalformedBindExpr);
+      return false;
+    }
+    if (OpenToken.Kind != TokenInfo::TK_OpenParen) {
+      Error->pushErrorFrame(OpenToken.Range, Error->ET_ParserMalformedBindExpr);
+      return false;
+    }
+    if (IDToken.Kind != TokenInfo::TK_Literal || !IDToken.Value.isString()) {
+      Error->pushErrorFrame(IDToken.Range, Error->ET_ParserMalformedBindExpr);
+      return false;
+    }
+    if (CloseToken.Kind != TokenInfo::TK_CloseParen) {
+      Error->pushErrorFrame(CloseToken.Range,
+                            Error->ET_ParserMalformedBindExpr);
+      return false;
+    }
+    BindID = IDToken.Value.getString();
+  }
+
   // Merge the start and end infos.
   SourceRange MatcherRange = NameToken.Range;
   MatcherRange.End = EndToken.Range.End;
-  DynTypedMatcher *Result =
-      S->actOnMatcherExpression(NameToken.Text, MatcherRange, Args, Error);
+  DynTypedMatcher *Result = S->actOnMatcherExpression(
+      NameToken.Text, MatcherRange, BindID, Args, Error);
   if (Result == NULL) {
     Error->pushErrorFrame(NameToken.Range, Error->ET_ParserMatcherFailure)
         << NameToken.Text;
@@ -271,6 +314,7 @@
   case TokenInfo::TK_OpenParen:
   case TokenInfo::TK_CloseParen:
   case TokenInfo::TK_Comma:
+  case TokenInfo::TK_Period:
   case TokenInfo::TK_InvalidChar:
     const TokenInfo Token = Tokenizer->consumeNextToken();
     Error->pushErrorFrame(Token.Range, Error->ET_ParserInvalidToken)
@@ -290,9 +334,15 @@
   virtual ~RegistrySema() {}
   DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName,
                                           const SourceRange &NameRange,
+                                          StringRef BindID,
                                           ArrayRef<ParserValue> Args,
                                           Diagnostics *Error) {
-    return Registry::constructMatcher(MatcherName, NameRange, Args, Error);
+    if (BindID.empty()) {
+      return Registry::constructMatcher(MatcherName, NameRange, Args, Error);
+    } else {
+      return Registry::constructBoundMatcher(MatcherName, NameRange, BindID,
+                                             Args, Error);
+    }
   }
 };
 
@@ -305,7 +355,13 @@
 bool Parser::parseExpression(StringRef Code, Sema *S,
                              VariantValue *Value, Diagnostics *Error) {
   CodeTokenizer Tokenizer(Code, Error);
-  return Parser(&Tokenizer, S, Error).parseExpressionImpl(Value);
+  if (!Parser(&Tokenizer, S, Error).parseExpressionImpl(Value)) return false;
+  if (Tokenizer.peekNextToken().Kind != TokenInfo::TK_Eof) {
+    Error->pushErrorFrame(Tokenizer.peekNextToken().Range,
+                          Error->ET_ParserTrailingCode);
+    return false;
+  }
+  return true;
 }
 
 DynTypedMatcher *Parser::parseMatcherExpression(StringRef Code,