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;