Merge pull request #912 from dart-lang/null-aware-index

Update to use the new null-aware subscript syntax.
diff --git a/.travis.yml b/.travis.yml
index 93d3428..1785982 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
 language: dart
 
 dart:
-  - 2.3.0
+  - 2.7.0
   - dev
 
 dart_task:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5983369..feb8afe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
 * Always place a blank line after script tags (#782).
 * Don't add unneeded splits on if elements near comments (#888).
 * Indent blocks in initializers of multiple-variable declarations.
+* Update the null-aware subscript syntax from `?.[]` to `?[]`.
 
 # 1.3.3
 
diff --git a/lib/src/source_visitor.dart b/lib/src/source_visitor.dart
index 6ffa262..46c0bf7 100644
--- a/lib/src/source_visitor.dart
+++ b/lib/src/source_visitor.dart
@@ -1180,11 +1180,11 @@
     token(node.semicolon);
   }
 
-  /// A period (`.`) token constructed to replace the given [operator].
+  /// Synthesize a token with [type] to replace the given [operator].
   ///
   /// Offset, comments, and previous/next links are all preserved.
-  static Token _period(Token operator) =>
-      Token(TokenType.PERIOD, operator.offset, operator.precedingComments)
+  static Token _synthesizeToken(TokenType type, Token operator) =>
+      Token(type, operator.offset, operator.precedingComments)
         ..previous = operator.previous
         ..next = operator.next;
 
@@ -1214,7 +1214,7 @@
           _insertCascadeTargetIntoExpression(expressionTarget, cascadeTarget),
           // If we've reached the end, replace the `..` operator with `.`
           expressionTarget == cascadeTarget
-              ? _period(expression.operator)
+              ? _synthesizeToken(TokenType.PERIOD, expression.operator)
               : expression.operator,
           expression.propertyName);
     } else if (expression is MethodInvocation) {
@@ -1222,17 +1222,28 @@
           _insertCascadeTargetIntoExpression(expressionTarget, cascadeTarget),
           // If we've reached the end, replace the `..` operator with `.`
           expressionTarget == cascadeTarget
-              ? _period(expression.operator)
+              ? _synthesizeToken(TokenType.PERIOD, expression.operator)
               : expression.operator,
           expression.methodName,
           expression.typeArguments,
           expression.argumentList);
     } else if (expression is IndexExpression) {
-      return astFactory.indexExpressionForTarget(
-          _insertCascadeTargetIntoExpression(expressionTarget, cascadeTarget),
-          expression.leftBracket,
-          expression.index,
-          expression.rightBracket);
+      var question = expression.question;
+
+      // A null-aware cascade treats the `?` in `?..` as part of the token, but
+      // for a non-cascade index, it is a separate `?` token.
+      if (expression.period != null &&
+          expression.period.type == TokenType.QUESTION_PERIOD_PERIOD) {
+        question = _synthesizeToken(TokenType.QUESTION, expression.period);
+      }
+
+      return astFactory.indexExpressionForTarget2(
+          target: _insertCascadeTargetIntoExpression(
+              expressionTarget, cascadeTarget),
+          question: question,
+          leftBracket: expression.leftBracket,
+          index: expression.index,
+          rightBracket: expression.rightBracket);
     }
     throw UnimplementedError('Unhandled ${expression.runtimeType}'
         '($expression)');
@@ -2061,6 +2072,7 @@
     }
 
     builder.startSpan(Cost.index);
+    token(node.question);
     token(node.leftBracket);
     soloZeroSplit();
     visit(node.index);
diff --git a/pubspec.lock b/pubspec.lock
index cd23c27..eea1964 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -7,14 +7,14 @@
       name: _fe_analyzer_shared
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.0.3"
+    version: "2.0.0"
   analyzer:
     dependency: "direct main"
     description:
       name: analyzer
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.39.4"
+    version: "0.39.5"
   archive:
     dependency: transitive
     description:
@@ -28,14 +28,14 @@
       name: args
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.5.3"
+    version: "1.6.0"
   async:
     dependency: transitive
     description:
       name: async
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.4.0"
+    version: "2.4.1"
   boolean_selector:
     dependency: transitive
     description:
@@ -77,7 +77,7 @@
       name: coverage
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.13.8"
+    version: "0.13.9"
   crypto:
     dependency: transitive
     description:
@@ -105,7 +105,7 @@
       name: grinder
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.8.3+1"
+    version: "0.8.4"
   html:
     dependency: transitive
     description:
@@ -133,14 +133,14 @@
       name: http_parser
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "3.1.3"
+    version: "3.1.4"
   io:
     dependency: transitive
     description:
       name: io
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.3.3"
+    version: "0.3.4"
   js:
     dependency: "direct dev"
     description:
@@ -210,7 +210,7 @@
       name: package_config
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.9.1"
+    version: "1.9.3"
   path:
     dependency: "direct main"
     description:
@@ -238,7 +238,7 @@
       name: pub_semver
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.4.3"
+    version: "1.4.4"
   shelf:
     dependency: transitive
     description:
@@ -287,7 +287,7 @@
       name: source_span
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.6.0"
+    version: "1.7.0"
   stack_trace:
     dependency: transitive
     description:
@@ -322,7 +322,7 @@
       name: test
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.14.1"
+    version: "1.14.2"
   test_api:
     dependency: transitive
     description:
@@ -336,7 +336,7 @@
       name: test_core
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.3.2"
+    version: "0.3.3"
   test_descriptor:
     dependency: "direct dev"
     description:
@@ -364,14 +364,14 @@
       name: vm_service
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "3.0.0+1"
+    version: "4.0.0"
   watcher:
     dependency: transitive
     description:
       name: watcher
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.9.7+13"
+    version: "0.9.7+14"
   web_socket_channel:
     dependency: transitive
     description:
diff --git a/pubspec.yaml b/pubspec.yaml
index bcb2d5c..9d60ca7 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -7,10 +7,10 @@
 homepage: https://github.com/dart-lang/dart_style
 
 environment:
-  sdk: '>=2.3.0 <3.0.0'
+  sdk: '>=2.7.0 <3.0.0'
 
 dependencies:
-  analyzer: ">=0.38.3 <0.40.0"
+  analyzer: ">=0.39.5 <0.40.0"
   args: ^1.0.0
   path: ^1.0.0
   source_span: ^1.4.0
diff --git a/test/fixes/single_cascade_statements.stmt b/test/fixes/single_cascade_statements.stmt
index 3ffe5a0..2c769c8 100644
--- a/test/fixes/single_cascade_statements.stmt
+++ b/test/fixes/single_cascade_statements.stmt
@@ -15,6 +15,18 @@
 obj..[subscript] = 3;
 <<<
 obj[subscript] = 3;
+>>>
+obj?..[subscript] = 3;
+<<<
+obj?[subscript] = 3;
+>>>
+obj..index[subscript] = 3;
+<<<
+obj.index[subscript] = 3;
+>>>
+obj..index?[subscript] = 3;
+<<<
+obj.index?[subscript] = 3;
 >>> targets that don't need parentheses
 {
   null..method();
diff --git a/test/splitting/expressions.stmt b/test/splitting/expressions.stmt
index 01fd186..3911eb1 100644
--- a/test/splitting/expressions.stmt
+++ b/test/splitting/expressions.stmt
@@ -132,11 +132,11 @@
         [longArgument][longArgument]
     [longArgument];
 >>> null-aware indexes
-identifier?.[longArgument][longArgument]?.[longArgument][longArgument]?.[longArgument];
+identifier?[longArgument][longArgument]?[longArgument][longArgument]?[longArgument];
 <<<
-identifier?.[longArgument][longArgument]
-        ?.[longArgument][longArgument]
-    ?.[longArgument];
+identifier?[longArgument][longArgument]
+        ?[longArgument][longArgument]
+    ?[longArgument];
 >>> is
 verylongIdentifier.property is LongTypeName;
 <<<
diff --git a/test/splitting/invocations.stmt b/test/splitting/invocations.stmt
index 702726e..17948be 100644
--- a/test/splitting/invocations.stmt
+++ b/test/splitting/invocations.stmt
@@ -434,11 +434,11 @@
 })!;
 >>> null-aware index
 receiver.property1.property2
-        .property3?.[0][1]?.[2]
-    .method1()?.[0][1]?.[2]
+        .property3?[0][1]?[2]
+    .method1()?[0][1]?[2]
     .method2();
 <<<
 receiver.property1.property2
-    .property3?.[0][1]?.[2]
-    .method1()?.[0][1]?.[2]
+    .property3?[0][1]?[2]
+    .method1()?[0][1]?[2]
     .method2();
\ No newline at end of file
diff --git a/test/whitespace/expressions.stmt b/test/whitespace/expressions.stmt
index 2514101..daff76b 100644
--- a/test/whitespace/expressions.stmt
+++ b/test/whitespace/expressions.stmt
@@ -160,6 +160,6 @@
 <<<
 obj![index]!(call)! + 3;
 >>> null-aware index expressions
-obj   ?.[  foo];
+obj   ?[  foo];
 <<<
-obj?.[foo];
\ No newline at end of file
+obj?[foo];
\ No newline at end of file