Format null-assertion operators. (#832)

* Format null-assertion operators.

In the process, I reorganized CallChainVisitor to more gracefully
handle the various combinations of selectors. It was getting a little
too hard to understand.

That also highlighted that we didn't have good support for invocation
expressions inside call chains. So I added better formatting for that
too.

* Make sure postfix expressions are preserved on block calls.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 993eafc..c204228 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+# 1.3.0
+
+* Format null assertion operators.
+* Better formatting for invocation expressions inside method call chains.
+
 # 1.2.9
 
 * Support `package:analyzer` `0.37.0`.
diff --git a/lib/src/call_chain_visitor.dart b/lib/src/call_chain_visitor.dart
index 7546e22..9b6b50b 100644
--- a/lib/src/call_chain_visitor.dart
+++ b/lib/src/call_chain_visitor.dart
@@ -13,8 +13,30 @@
 import 'source_visitor.dart';
 
 /// Helper class for [SourceVisitor] that handles visiting and writing a
-/// chained series of method invocations, property accesses, and/or prefix
-/// expressions. In other words, anything using the "." operator.
+/// chained series of "selectors": method invocations, property accesses,
+/// prefixed identifiers, index expressions, and null-assertion operators.
+///
+/// In the AST, selectors are nested bottom up such that this expression:
+///
+///     obj.a(1)[2].c(3)
+///
+/// Is structured like:
+///
+///           .c()
+///           /  \
+///          []   3
+///         /  \
+///       .a()  2
+///       /  \
+///     obj   1
+///
+/// This means visiting the AST from top down visits the selectors from right
+/// to left. It's easier to format that if we organize them as a linear series
+/// of selectors from left to right. Further, we want to organize it into a
+/// two-tier hierarchy. We have an outer list of method calls and property
+/// accesses. Then each of those may have one or more postfix selectors
+/// attached: indexers, null-assertions, or invocations. This mirrors how they
+/// are formatted.
 class CallChainVisitor {
   final SourceVisitor _visitor;
 
@@ -24,15 +46,15 @@
   /// [PrefixedIdentifier].
   final Expression _target;
 
-  /// The list of dotted names ([PropertyAccess] and [PrefixedIdentifier] at
+  /// The list of dotted names ([PropertyAccess] and [PrefixedIdentifier]) at
   /// the start of the call chain.
   ///
   /// This will be empty if the [_target] is not a [SimpleIdentifier].
-  final List<Expression> _properties;
+  final List<_Selector> _properties;
 
   /// The mixed method calls and property accesses in the call chain in the
-  /// order that they appear in the source.
-  final List<Expression> _calls;
+  /// order that they appear in the source reading from left to right.
+  final List<_Selector> _calls;
 
   /// The method calls containing block function literals that break the method
   /// chain and escape its indentation.
@@ -59,7 +81,7 @@
   ///         })
   ///         .d()
   ///         .e();
-  final List<Expression> _blockCalls;
+  final List<_MethodSelector> _blockCalls;
 
   /// If there is one or more block calls and a single chained expression after
   /// that, this will be that expression.
@@ -74,7 +96,7 @@
   /// need to split before its `.` and this accommodates the common pattern of
   /// a trailing `toList()` or `toSet()` after a series of higher-order methods
   /// on an iterable.
-  final Expression _hangingCall;
+  final _MethodSelector _hangingCall;
 
   /// Whether or not a [Rule] is currently active for the call chain.
   bool _ruleEnabled = false;
@@ -86,49 +108,17 @@
   /// rule used to split between them.
   PositionalRule _propertyRule;
 
-  /// Creates a new call chain visitor for [visitor] starting with [node].
+  /// Creates a new call chain visitor for [visitor] for the method chain
+  /// contained in [node].
   ///
   /// The [node] is the outermost expression containing the chained "."
   /// operators and must be a [MethodInvocation], [PropertyAccess] or
   /// [PrefixedIdentifier].
   factory CallChainVisitor(SourceVisitor visitor, Expression node) {
-    Expression target;
-
-    // Recursively walk the chain of calls and turn the tree into a list.
-    var calls = <Expression>[];
-    flatten(Expression expression) {
-      target = expression;
-
-      // Treat index expressions where the target is a valid call in a method
-      // chain as being part of the call. Handles cases like:
-      //
-      //     receiver
-      //         .property
-      //         .property[0]
-      //         .property
-      //         .method()[1][2];
-      var call = expression;
-      while (call is IndexExpression) {
-        call = (call as IndexExpression).target;
-      }
-
-      if (SourceVisitor.looksLikeStaticCall(call)) {
-        // Don't include things that look like static method or constructor
-        // calls in the call chain because that tends to split up named
-        // constructors from their class.
-      } else if (call is MethodInvocation && call.target != null) {
-        flatten(call.target);
-        calls.add(expression);
-      } else if (call is PropertyAccess && call.target != null) {
-        flatten(call.target);
-        calls.add(expression);
-      } else if (call is PrefixedIdentifier) {
-        flatten(call.prefix);
-        calls.add(expression);
-      }
-    }
-
-    flatten(node);
+    // Flatten the call chain tree to a list of selectors with postfix
+    // expressions.
+    var calls = <_Selector>[];
+    var target = _unwrapTarget(node, calls);
 
     // An expression that starts with a series of dotted names gets treated a
     // little specially. We don't force leading properties to split with the
@@ -137,35 +127,22 @@
     //     address.street.number
     //       .toString()
     //       .length;
-    var properties = <Expression>[];
-    if (target is SimpleIdentifier) {
-      properties = calls.takeWhile((call) {
-        // Step into index expressions to see what the index is on.
-        while (call is IndexExpression) {
-          call = (call as IndexExpression).target;
-        }
-        return call is! MethodInvocation;
-      }).toList();
+    var properties = <_Selector>[];
+    if (_unwrapNullAssertion(target) is SimpleIdentifier) {
+      properties = calls.takeWhile((call) => call.isProperty).toList();
     }
 
     calls.removeRange(0, properties.length);
 
     // Separate out the block calls, if there are any.
-    List<Expression> blockCalls;
-    var hangingCall;
+    List<_MethodSelector> blockCalls;
+    _MethodSelector hangingCall;
 
     var inBlockCalls = false;
     for (var call in calls) {
-      // See if this call is a method call whose arguments are block formatted.
-      var isBlockCall = false;
-      if (call is MethodInvocation) {
-        var args = ArgumentListVisitor(visitor, call.argumentList);
-        isBlockCall = args.hasBlockArguments;
-      }
-
-      if (isBlockCall) {
+      if (call.isBlockCall(visitor)) {
         inBlockCalls = true;
-        if (blockCalls == null) blockCalls = [];
+        blockCalls ??= [];
         blockCalls.add(call);
       } else if (inBlockCalls) {
         // We found a non-block call after a block call.
@@ -231,7 +208,7 @@
     // before one ".", or before all of them.
     if (_properties.length == 1) {
       _visitor.soloZeroSplit();
-      _writeCall(_properties.single);
+      _properties.single.write(this);
     } else if (_properties.length > 1) {
       if (!splitOnTarget) {
         _propertyRule = PositionalRule(null, 0, 0);
@@ -240,7 +217,7 @@
 
       for (var property in _properties) {
         _propertyRule.beforeArgument(_visitor.zeroSplit());
-        _writeCall(property);
+        property.write(this);
       }
 
       _visitor.builder.endRule();
@@ -261,7 +238,7 @@
     for (var call in _calls) {
       _enableRule();
       _visitor.zeroSplit();
-      _writeCall(call);
+      call.write(this);
     }
 
     if (_calls.length > 1) _visitor.builder.endBlockArgumentNesting();
@@ -274,14 +251,12 @@
       _disableRule();
 
       for (var blockCall in _blockCalls) {
-        _writeBlockCall(blockCall);
+        blockCall.write(this);
       }
 
       // If there is a hanging call after the last block, write it without any
       // split before the ".".
-      if (_hangingCall != null) {
-        _writeCall(_hangingCall);
-      }
+      _hangingCall?.write(this);
     }
 
     _disableRule();
@@ -356,38 +331,16 @@
     return _forcesSplit(argument);
   }
 
-  /// Writes [call], which must be one of the supported expression types.
-  void _writeCall(Expression call) {
-    if (call is IndexExpression) {
-      _visitor.builder.nestExpression();
-      _writeCall(call.target);
-      _visitor.finishIndexExpression(call);
-      _visitor.builder.unnest();
-    } else if (call is MethodInvocation) {
-      _writeInvocation(call);
-    } else if (call is PropertyAccess) {
-      _visitor.token(call.operator);
-      _visitor.visit(call.propertyName);
-    } else if (call is PrefixedIdentifier) {
-      _visitor.token(call.period);
-      _visitor.visit(call.identifier);
-    } else {
-      // Unexpected type.
-      assert(false);
-    }
-  }
-
-  void _writeInvocation(MethodInvocation invocation) {
-    _visitor.token(invocation.operator);
-    _visitor.token(invocation.methodName.token);
-
+  /// Called when a [_MethodSelector] has written its name and is about to
+  /// write the argument list.
+  void _beforeMethodArguments(_MethodSelector selector) {
     // If we don't have any block calls, stop the rule after the last method
     // call name, but before its arguments. This allows unsplit chains where
     // the last argument list wraps, like:
     //
     //     foo().bar().baz(
     //         argument, list);
-    if (_blockCalls == null && _calls.isNotEmpty && invocation == _calls.last) {
+    if (_blockCalls == null && _calls.isNotEmpty && selector == _calls.last) {
       _disableRule();
     }
 
@@ -413,18 +366,6 @@
         _target is SimpleIdentifier) {
       _endSpan();
     }
-
-    _visitor.builder.nestExpression();
-    _visitor.visit(invocation.typeArguments);
-    _visitor.visitArgumentList(invocation.argumentList, nestExpression: false);
-    _visitor.builder.unnest();
-  }
-
-  void _writeBlockCall(MethodInvocation invocation) {
-    _visitor.token(invocation.operator);
-    _visitor.token(invocation.methodName.token);
-    _visitor.visit(invocation.typeArguments);
-    _visitor.visit(invocation.argumentList);
   }
 
   /// If a [Rule] for the method chain is currently active, ends it.
@@ -460,3 +401,190 @@
     _spanEnded = true;
   }
 }
+
+/// One "selector" in a method call chain.
+///
+/// Each selector is a method call or property access. It may be followed by
+/// one or more postfix expressions, which can be index expressions or
+/// null-assertion operators. These are not treated like their own selectors
+/// because the formatter attaches them to the previous method call or property
+/// access:
+///
+///     receiver
+///         .method(arg)[index]
+///         .another()!
+///         .third();
+abstract class _Selector {
+  /// The series of index and/or null-assertion postfix selectors that follow
+  /// and are attached to this one.
+  ///
+  /// Elements in this list will either be [IndexExpression] or
+  /// [PostfixExpression].
+  final List<Expression> _postfixes = [];
+
+  /// Whether this selector is a property access as opposed to a method call.
+  bool get isProperty => true;
+
+  /// Whether this selector is a method call whose arguments are block
+  /// formatted.
+  bool isBlockCall(SourceVisitor visitor) => false;
+
+  /// Write the selector portion of the expression wrapped by this [_Selector]
+  /// using [visitor], followed by any postfix selectors.
+  void write(CallChainVisitor visitor) {
+    writeSelector(visitor);
+
+    // Write any trailing index and null-assertion operators.
+    visitor._visitor.builder.nestExpression();
+    for (var postfix in _postfixes) {
+      if (postfix is FunctionExpressionInvocation) {
+        // Allow splitting between the invocations if needed.
+        visitor._visitor.soloZeroSplit();
+
+        visitor._visitor.visit(postfix.typeArguments);
+        visitor._visitor.visitArgumentList(postfix.argumentList);
+      } else if (postfix is IndexExpression) {
+        visitor._visitor.finishIndexExpression(postfix);
+      } else if (postfix is PostfixExpression) {
+        assert(postfix.operator.type == TokenType.BANG);
+        visitor._visitor.token(postfix.operator);
+      } else {
+        // Unexpected type.
+        assert(false);
+      }
+    }
+    visitor._visitor.builder.unnest();
+  }
+
+  /// Subclasses implement this to write their selector.
+  void writeSelector(CallChainVisitor visitor);
+}
+
+class _MethodSelector extends _Selector {
+  final MethodInvocation _node;
+
+  _MethodSelector(this._node);
+
+  bool get isProperty => false;
+
+  bool isBlockCall(SourceVisitor visitor) =>
+      ArgumentListVisitor(visitor, _node.argumentList).hasBlockArguments;
+
+  void writeSelector(CallChainVisitor visitor) {
+    visitor._visitor.token(_node.operator);
+    visitor._visitor.token(_node.methodName.token);
+
+    visitor._beforeMethodArguments(this);
+
+    visitor._visitor.builder.nestExpression();
+    visitor._visitor.visit(_node.typeArguments);
+    visitor._visitor
+        .visitArgumentList(_node.argumentList, nestExpression: false);
+    visitor._visitor.builder.unnest();
+  }
+}
+
+class _PrefixedSelector extends _Selector {
+  final PrefixedIdentifier _node;
+
+  _PrefixedSelector(this._node);
+
+  void writeSelector(CallChainVisitor visitor) {
+    visitor._visitor.token(_node.period);
+    visitor._visitor.visit(_node.identifier);
+  }
+}
+
+class _PropertySelector extends _Selector {
+  final PropertyAccess _node;
+
+  _PropertySelector(this._node);
+
+  void writeSelector(CallChainVisitor visitor) {
+    visitor._visitor.token(_node.operator);
+    visitor._visitor.visit(_node.propertyName);
+  }
+}
+
+/// If [expression] is a null-assertion operator, returns its operand.
+Expression _unwrapNullAssertion(Expression expression) {
+  if (expression is PostfixExpression &&
+      expression.operator.type == TokenType.BANG) {
+    return expression.operand;
+  }
+
+  return expression;
+}
+
+/// Given [node], which is the outermost expression for some call chain,
+/// recursively traverses the selectors to fill in the list of [calls].
+///
+/// Returns the remaining target expression that precedes the method chain.
+/// For example, given:
+///
+///     foo.bar()!.baz[0][1].bang()
+///
+/// This returns `foo` and fills calls with:
+///
+///     selector  postfixes
+///     --------  ---------
+///     .bar()    !
+///     .baz      [0], [1]
+///     .bang()
+Expression _unwrapTarget(Expression node, List<_Selector> calls) {
+  // Don't include things that look like static method or constructor
+  // calls in the call chain because that tends to split up named
+  // constructors from their class.
+  if (SourceVisitor.looksLikeStaticCall(node)) return node;
+
+  // Selectors.
+  if (node is MethodInvocation && node.target != null) {
+    return _unwrapSelector(node.target, _MethodSelector(node), calls);
+  }
+
+  if (node is PropertyAccess && node.target != null) {
+    return _unwrapSelector(node.target, _PropertySelector(node), calls);
+  }
+
+  if (node is PrefixedIdentifier) {
+    return _unwrapSelector(node.prefix, _PrefixedSelector(node), calls);
+  }
+
+  // Postfix expressions.
+  if (node is IndexExpression) {
+    return _unwrapPostfix(node, node.target, calls);
+  }
+
+  if (node is FunctionExpressionInvocation) {
+    return _unwrapPostfix(node, node.function, calls);
+  }
+
+  if (node is PostfixExpression && node.operator.type == TokenType.BANG) {
+    return _unwrapPostfix(node, node.operand, calls);
+  }
+
+  // Otherwise, it isn't a selector so we're done.
+  return node;
+}
+
+Expression _unwrapPostfix(
+    Expression node, Expression target, List<_Selector> calls) {
+  target = _unwrapTarget(target, calls);
+
+  // If we don't have a preceding selector to hang the postfix expression off
+  // of, don't unwrap it and leave it attached to the target expression. For
+  // example:
+  //
+  //     (list + another)[index]
+  if (calls.isEmpty) return node;
+
+  calls.last._postfixes.add(node);
+  return target;
+}
+
+Expression _unwrapSelector(
+    Expression target, _Selector selector, List<_Selector> calls) {
+  target = _unwrapTarget(target, calls);
+  calls.add(selector);
+  return target;
+}
diff --git a/lib/src/dart_formatter.dart b/lib/src/dart_formatter.dart
index 2cf93c4..478e6a7 100644
--- a/lib/src/dart_formatter.dart
+++ b/lib/src/dart_formatter.dart
@@ -93,7 +93,7 @@
     // version.
     // TODO(paulberry): consider plumbing in experiment enable flags from the
     // command line.
-    var featureSet = FeatureSet.fromEnableFlags([]);
+    var featureSet = FeatureSet.fromEnableFlags(["non-nullable"]);
 
     // Tokenize the source.
     var reader = CharSequenceReader(source.text);
diff --git a/pubspec.lock b/pubspec.lock
index 1101ad1..06dd6f0 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -28,7 +28,7 @@
       name: async
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.2.0"
+    version: "2.3.0"
   boolean_selector:
     dependency: transitive
     description:
@@ -98,7 +98,7 @@
       name: grinder
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.8.3"
+    version: "0.8.3+1"
   html:
     dependency: transitive
     description:
@@ -182,7 +182,7 @@
       name: node_preamble
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.4.4"
+    version: "1.4.6"
   package_config:
     dependency: transitive
     description:
@@ -203,7 +203,7 @@
       name: path
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.6.2"
+    version: "1.6.4"
   pedantic:
     dependency: "direct dev"
     description:
@@ -301,7 +301,7 @@
       name: string_scanner
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.0.4"
+    version: "1.0.5"
   term_glyph:
     dependency: transitive
     description:
@@ -357,21 +357,21 @@
       name: vm_service_lib
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "3.21.0"
+    version: "3.22.2+1"
   watcher:
     dependency: transitive
     description:
       name: watcher
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.9.7+10"
+    version: "0.9.7+12"
   web_socket_channel:
     dependency: transitive
     description:
       name: web_socket_channel
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.0.13"
+    version: "1.0.14"
   yaml:
     dependency: "direct dev"
     description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 71eb65a..4f69d8d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
 name: dart_style
 # Note: See tool/grind.dart for how to bump the version.
-version: 1.2.9
+version: 1.3.0-dev
 author: Dart Team <misc@dartlang.org>
 description: >-
   Opinionated, automatic Dart source code formatter.
@@ -11,7 +11,7 @@
   sdk: '>=2.3.0 <3.0.0'
 
 dependencies:
-  analyzer: '>=0.36.3 <0.38.0'
+  analyzer: '>=0.37.0 <0.38.0'
   args: '>=0.12.1 <2.0.0'
   path: ^1.0.0
   source_span: ^1.4.0
diff --git a/test/splitting/invocations.stmt b/test/splitting/invocations.stmt
index 1be94f3..071b3a8 100644
--- a/test/splitting/invocations.stmt
+++ b/test/splitting/invocations.stmt
@@ -209,6 +209,14 @@
     .property4
     .property5
     .property6;
+>>> index on block calls
+receiver.method(() {;})[0].block(() {;})[1];
+<<<
+receiver.method(() {
+  ;
+})[0].block(() {
+  ;
+})[1];
 >>> target splits more deeply than method chain
 someTargetFunction(argument, argument, argument).method().method();
 <<<
@@ -342,4 +350,65 @@
     .method()
     .method()
     .method()
-    .method();
\ No newline at end of file
+    .method();
+>>> invocations in method chain
+someReceiverObject.method1().method2().method3()(argument)
+.method4().method5().method6();
+<<<
+someReceiverObject
+    .method1()
+    .method2()
+    .method3()(argument)
+    .method4()
+    .method5()
+    .method6();
+>>> chained invocations
+someReceiverObject.method1().method2().method3()
+(argument)(argument)<T, R>(argument, argument, argument, argument, argument)(argument)
+.method4().method5().method6();
+<<<
+someReceiverObject
+    .method1()
+    .method2()
+    .method3()(argument)(argument)
+        <T, R>(
+            argument,
+            argument,
+            argument,
+            argument,
+            argument)(argument)
+    .method4()
+    .method5()
+    .method6();
+>>> "!" stays with operand before method call
+verylongIdentifier!.longIdentifier().another()!.aThird()!;
+<<<
+verylongIdentifier!
+    .longIdentifier()
+    .another()!
+    .aThird()!;
+>>> "!" stays with operand before property access
+verylongIdentifier!.longIdentifier.another!.aThird!;
+<<<
+verylongIdentifier!
+    .longIdentifier.another!.aThird!;
+>>> "!" stays with operand before property access
+verylongIdentifier!.longIdentifier.another!.aThird!.longerPropertyChain;
+<<<
+verylongIdentifier!
+    .longIdentifier
+    .another!
+    .aThird!
+    .longerPropertyChain;
+>>> do not split null-asserted chained calls if not needed
+compiler!.a().b()!.c.d();
+<<<
+compiler!.a().b()!.c.d();
+>>> null-aware in block calls
+compiler!.a(() {;})!.b(() {;})!;
+<<<
+compiler!.a(() {
+  ;
+})!.b(() {
+  ;
+})!;
\ No newline at end of file
diff --git a/test/whitespace/expressions.stmt b/test/whitespace/expressions.stmt
index 10980d5..7dd8ded 100644
--- a/test/whitespace/expressions.stmt
+++ b/test/whitespace/expressions.stmt
@@ -134,4 +134,22 @@
 >>> inside interpolation
 " ${   interp+olate } and ${fn  (  1 ) } end";
 <<<
-" ${interp + olate} and ${fn(1)} end";
\ No newline at end of file
+" ${interp + olate} and ${fn(1)} end";
+>>> simple null assertion
+obj ! ;
+<<<
+obj!;
+>>> trailing method
+obj . method() ! ;
+<<<
+obj.method()!;
+>>> trailing property
+obj . prop ! ;
+<<<
+obj.prop!;
+>>> null assertion in method chain
+obj ! . getter ! . method ( arg ) ! + 3;
+// TODO: ![ and !(
+<<<
+obj!.getter!.method(arg)! + 3;
+// TODO: ![ and !(
\ No newline at end of file