update scanner/parser to handle null-aware index operator

This adds support for the nnbd `?.[` operator as specified in
https://github.com/dart-lang/language/pull/293/files#diff-dbeaf678924df9f8a2ca9f5d12e26d3eR11513

Change-Id: I11547cf875439fef9fe22d4f4ac5ab912b3759d1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/105902
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index e9c51f7..abfe057 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -2482,7 +2482,8 @@
   }
 
   void handleIndexedExpression(Token leftBracket, Token rightBracket) {
-    assert(optional('[', leftBracket));
+    assert(optional('[', leftBracket) ||
+        (enableNonNullable && optional('?.[', leftBracket)));
     assert(optional(']', rightBracket));
     debugEvent("IndexedExpression");
 
diff --git a/pkg/analyzer/test/generated/parser_fasta_test.dart b/pkg/analyzer/test/generated/parser_fasta_test.dart
index 8c8d903..6193bb2 100644
--- a/pkg/analyzer/test/generated/parser_fasta_test.dart
+++ b/pkg/analyzer/test/generated/parser_fasta_test.dart
@@ -2456,6 +2456,37 @@
     parseCompilationUnit('main() { C.a? Function()? x = 7; }');
   }
 
+  void test_indexed() {
+    CompilationUnit unit = parseCompilationUnit('main() { a[7]; }');
+    FunctionDeclaration method = unit.declarations[0];
+    BlockFunctionBody body = method.functionExpression.body;
+    ExpressionStatement statement = body.block.statements[0];
+    IndexExpression expression = statement.expression;
+    expect(expression.leftBracket.lexeme, '[');
+  }
+
+  void test_indexed_nullAware() {
+    CompilationUnit unit = parseCompilationUnit('main() { a?.[7]; }');
+    FunctionDeclaration method = unit.declarations[0];
+    BlockFunctionBody body = method.functionExpression.body;
+    ExpressionStatement statement = body.block.statements[0];
+    IndexExpression expression = statement.expression;
+    expect(expression.leftBracket.lexeme, '?.[');
+  }
+
+  void test_indexed_nullAware_optOut() {
+    CompilationUnit unit = parseCompilationUnit('''
+// @dart = 2.2
+main() { a?.[7]; }''',
+        errors: [expectedError(ParserErrorCode.MISSING_IDENTIFIER, 27, 1)]);
+    FunctionDeclaration method = unit.declarations[0];
+    BlockFunctionBody body = method.functionExpression.body;
+    ExpressionStatement statement = body.block.statements[0];
+    PropertyAccess expression = statement.expression;
+    expect(expression.target.toSource(), 'a');
+    expect(expression.operator.lexeme, '?.');
+  }
+
   void test_is_nullable() {
     CompilationUnit unit =
         parseCompilationUnit('main() { x is String? ? (x + y) : z; }');
diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart
index 8bf97b2..4aa274e 100644
--- a/pkg/front_end/lib/src/fasta/parser/parser.dart
+++ b/pkg/front_end/lib/src/fasta/parser/parser.dart
@@ -4009,8 +4009,9 @@
             token = parsePrimary(
                 token.next, IdentifierContext.expressionContinuation);
             listener.endBinaryExpression(operator);
-          } else if ((identical(type, TokenType.OPEN_PAREN)) ||
-              (identical(type, TokenType.OPEN_SQUARE_BRACKET))) {
+          } else if (identical(type, TokenType.OPEN_PAREN) ||
+              identical(type, TokenType.OPEN_SQUARE_BRACKET) ||
+              identical(type, TokenType.QUESTION_PERIOD_OPEN_SQUARE_BRACKET)) {
             token = parseArgumentOrIndexStar(token, typeArg);
           } else if (identical(type, TokenType.INDEX)) {
             BeginToken replacement = link(
@@ -4180,7 +4181,7 @@
     Token next = token.next;
     Token beginToken = next;
     while (true) {
-      if (optional('[', next)) {
+      if (optional('[', next) || optional('?.[', next)) {
         assert(typeArg == noTypeParamOrArg);
         Token openSquareBracket = next;
         bool old = mayParseFunctionExpressions;
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 e59d613..f7b4d39 100644
--- a/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
@@ -564,8 +564,13 @@
       return select(
           $EQ, TokenType.QUESTION_QUESTION_EQ, TokenType.QUESTION_QUESTION);
     } else if (identical(next, $PERIOD)) {
+      next = advance();
+      if (_enableNonNullable && identical($OPEN_SQUARE_BRACKET, next)) {
+        appendPrecedenceToken(TokenType.QUESTION_PERIOD_OPEN_SQUARE_BRACKET);
+        return advance();
+      }
       appendPrecedenceToken(TokenType.QUESTION_PERIOD);
-      return advance();
+      return next;
     } else {
       appendPrecedenceToken(TokenType.QUESTION);
       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 fff742e..e468145 100644
--- a/pkg/front_end/lib/src/fasta/scanner/token_constants.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/token_constants.dart
@@ -89,3 +89,4 @@
 const int GT_GT_GT_TOKEN = GENERIC_METHOD_TYPE_LIST_TOKEN + 1;
 const int PERIOD_PERIOD_PERIOD_QUESTION_TOKEN = GT_GT_GT_TOKEN + 1;
 const int GT_GT_GT_EQ_TOKEN = PERIOD_PERIOD_PERIOD_QUESTION_TOKEN + 1;
+const int QUESTION_PERIOD_OPEN_SQUARE_BRACKET_TOKEN = GT_GT_GT_EQ_TOKEN + 1;
diff --git a/pkg/front_end/lib/src/scanner/token.dart b/pkg/front_end/lib/src/scanner/token.dart
index 5947f80..47ab0d1 100644
--- a/pkg/front_end/lib/src/scanner/token.dart
+++ b/pkg/front_end/lib/src/scanner/token.dart
@@ -1443,6 +1443,12 @@
       NO_PRECEDENCE,
       PERIOD_PERIOD_PERIOD_QUESTION_TOKEN);
 
+  static const TokenType QUESTION_PERIOD_OPEN_SQUARE_BRACKET = const TokenType(
+      '?.[',
+      'QUESTION_PERIOD_OPEN_SQUARE_BRACKET',
+      SELECTOR_PRECEDENCE,
+      QUESTION_PERIOD_OPEN_SQUARE_BRACKET_TOKEN);
+
   static const TokenType AS = Keyword.AS;
 
   static const TokenType IS = Keyword.IS;