Add spread collection token to scanner

Change-Id: I1932d7c7fa6daba244ad2d93ddc7e2de18100fee
Reviewed-on: https://dart-review.googlesource.com/c/89600
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
diff --git a/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart b/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
index 6ac7640..3377316 100644
--- a/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
@@ -736,8 +736,20 @@
     if (($0 <= next && next <= $9)) {
       return tokenizeFractionPart(next, start);
     } else if (identical($PERIOD, next)) {
-      return select(
-          $PERIOD, TokenType.PERIOD_PERIOD_PERIOD, TokenType.PERIOD_PERIOD);
+      next = advance();
+      if (identical(next, $PERIOD)) {
+        next = advance();
+        if (identical(next, $QUESTION)) {
+          appendPrecedenceToken(TokenType.PERIOD_PERIOD_PERIOD_QUESTION);
+          return advance();
+        } else {
+          appendPrecedenceToken(TokenType.PERIOD_PERIOD_PERIOD);
+          return next;
+        }
+      } else {
+        appendPrecedenceToken(TokenType.PERIOD_PERIOD);
+        return next;
+      }
     } else {
       appendPrecedenceToken(TokenType.PERIOD);
       return next;
diff --git a/pkg/front_end/lib/src/fasta/scanner/token_constants.dart b/pkg/front_end/lib/src/fasta/scanner/token_constants.dart
index 6ac1842..f29765c 100644
--- a/pkg/front_end/lib/src/fasta/scanner/token_constants.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/token_constants.dart
@@ -87,3 +87,4 @@
 const int GENERIC_METHOD_TYPE_ASSIGN_TOKEN = QUESTION_QUESTION_EQ_TOKEN + 1;
 const int GENERIC_METHOD_TYPE_LIST_TOKEN = GENERIC_METHOD_TYPE_ASSIGN_TOKEN + 1;
 const int GT_GT_GT_TOKEN = GENERIC_METHOD_TYPE_LIST_TOKEN + 1;
+const int PERIOD_PERIOD_PERIOD_QUESTION_TOKEN = GT_GT_GT_TOKEN + 1;
diff --git a/pkg/front_end/lib/src/scanner/token.dart b/pkg/front_end/lib/src/scanner/token.dart
index 119006f..3b01015 100644
--- a/pkg/front_end/lib/src/scanner/token.dart
+++ b/pkg/front_end/lib/src/scanner/token.dart
@@ -1422,6 +1422,12 @@
   static const TokenType PERIOD_PERIOD_PERIOD = const TokenType(
       '...', 'PERIOD_PERIOD_PERIOD', NO_PRECEDENCE, PERIOD_PERIOD_PERIOD_TOKEN);
 
+  static const TokenType PERIOD_PERIOD_PERIOD_QUESTION = const TokenType(
+      '...?',
+      'PERIOD_PERIOD_PERIOD_QUESTION',
+      NO_PRECEDENCE,
+      PERIOD_PERIOD_PERIOD_QUESTION_TOKEN);
+
   static const TokenType AS = Keyword.AS;
 
   static const TokenType IS = Keyword.IS;
@@ -1513,6 +1519,7 @@
     TokenType.BACKPING,
     TokenType.BACKSLASH,
     TokenType.PERIOD_PERIOD_PERIOD,
+    TokenType.PERIOD_PERIOD_PERIOD_QUESTION,
 
     // TODO(danrubel): Should these be added to the "all" list?
     //TokenType.IS,
diff --git a/pkg/front_end/test/precedence_info_test.dart b/pkg/front_end/test/precedence_info_test.dart
index 3d6ee26..e8fc1eb 100644
--- a/pkg/front_end/test/precedence_info_test.dart
+++ b/pkg/front_end/test/precedence_info_test.dart
@@ -357,6 +357,7 @@
     assertName('`', 'BACKPING');
     assertName('\\', 'BACKSLASH');
     assertName('...', 'PERIOD_PERIOD_PERIOD');
+    assertName('...?', 'PERIOD_PERIOD_PERIOD_QUESTION');
   }
 
   /// Assert precedence as per the Dart language spec
diff --git a/pkg/front_end/test/scanner_fasta_test.dart b/pkg/front_end/test/scanner_fasta_test.dart
index 4b68911..1305b34 100644
--- a/pkg/front_end/test/scanner_fasta_test.dart
+++ b/pkg/front_end/test/scanner_fasta_test.dart
@@ -324,6 +324,15 @@
     }
   }
 
+  void test_spread_operators() {
+    ErrorListener listener = new ErrorListener();
+    Token openBracket = scanWithListener('[ 1, ...[2], ...?[3], ]', listener);
+    Token spreadToken = openBracket.next.next.next;
+    expect(spreadToken.lexeme, '...');
+    Token spreadQToken = spreadToken.next.next.next.next.next;
+    expect(spreadQToken.lexeme, '...?');
+  }
+
   @override
   void test_unmatched_openers() {
     ErrorListener listener = new ErrorListener();