Breaking analyzer change: always insert ImplicitCallReference nodes.

Previously, we only inserted these nodes when the
`constructor-tearoffs` feature was active, as a way of reducing the
risk of breaking analyzer clients; however this behavioral
inconsistency is not something we want to keep for the long term.

Note: even though this is technically a breaking change, we haven't
found any analyzer clients that are affected by it, so we're going
ahead and landing it without an analyzer version number bump.

Change-Id: I71f0fb2862b644dd1a81245bd12f5b7b9ca45857
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/233653
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
index 0aa6a8f..6ae7fe4 100644
--- a/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
@@ -66,10 +66,7 @@
 
     var callInsertion = _resolver.insertImplicitCallReference(initializer);
     if (callInsertion != null) {
-      var insertedExpression = callInsertion.expression;
-      if (insertedExpression != null) {
-        initializer = callInsertion.expression;
-      }
+      initializer = callInsertion.expression;
     }
 
     // Initializers of top-level variables and fields are already included
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index d42e16a..4e32f72 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -81,14 +81,8 @@
 /// Data structure describing the result of inserting an implicit call reference
 /// into the AST.
 class ImplicitCallInsertionResult {
-  /// The expression that was inserted, or `null`, if no expression was
-  /// inserted.
-  ///
-  /// The only reason this might be `null` is that, at the moment, we only
-  /// insert implicit call reference expressions if the 'constructor-tearoffs'
-  /// feature is enabled (to avoid breaking clients).
-  /// TODO(paulberry): make this non-nullable when we change this behavior.
-  final ImplicitCallReferenceImpl? expression;
+  /// The expression that was inserted.
+  final ImplicitCallReferenceImpl expression;
 
   /// The type of the implicit call tear-off.
   final FunctionType staticType;
@@ -862,14 +856,6 @@
       typeArgumentTypes = [];
     }
 
-    if (!isConstructorTearoffsEnabled) {
-      // Temporarily, only create [ImplicitCallReference] nodes under the
-      // 'constructor-tearoffs' feature.
-      // TODO(srawlins, paulberry): When we are ready to make a breaking change
-      // release to the analyzer package, remove this exception.
-      return ImplicitCallInsertionResult(null, callMethodType);
-    }
-
     var callReference = astFactory.implicitCallReference(
       expression: expression,
       staticElement: callMethod,
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index d79ce34..ed5ca6e 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -1151,6 +1151,18 @@
   }
 
   @override
+  DecoratedType? visitImplicitCallReference(ImplicitCallReference node) {
+    return _handlePropertyAccessGeneralized(
+        node: node,
+        target: node.expression,
+        propertyName: 'call',
+        isNullAware: false,
+        isCascaded: false,
+        inSetterContext: false,
+        callee: node.staticElement);
+  }
+
+  @override
   DecoratedType? visitIndexExpression(IndexExpression node) {
     DecoratedType? targetType;
     var target = node.target;
@@ -3112,24 +3124,43 @@
 
   DecoratedType? _handlePropertyAccess(Expression node, Expression? target,
       SimpleIdentifier propertyName, bool isNullAware, bool isCascaded) {
-    DecoratedType? targetType;
+    if (!isCascaded && _isPrefix(target)) {
+      return _dispatch(propertyName, skipNullCheckHint: true);
+    }
     var callee = getWriteOrReadElement(propertyName);
+    return _handlePropertyAccessGeneralized(
+        node: node,
+        target: target,
+        propertyName: propertyName.name,
+        isNullAware: isNullAware,
+        isCascaded: isCascaded,
+        inSetterContext: propertyName.inSetterContext(),
+        callee: callee);
+  }
+
+  DecoratedType? _handlePropertyAccessGeneralized(
+      {required Expression node,
+      required Expression? target,
+      required String propertyName,
+      required bool isNullAware,
+      required bool isCascaded,
+      required bool inSetterContext,
+      required Element? callee}) {
+    DecoratedType? targetType;
     bool calleeIsStatic = callee is ExecutableElement && callee.isStatic;
     if (isCascaded) {
       targetType = _currentCascadeTargetType;
-    } else if (_isPrefix(target)) {
-      return _dispatch(propertyName, skipNullCheckHint: true);
     } else if (calleeIsStatic) {
       _dispatch(target);
     } else if (isNullAware) {
       targetType = _dispatch(target);
     } else {
-      targetType = _handleTarget(target, propertyName.name, callee);
+      targetType = _handleTarget(target, propertyName, callee);
     }
     DecoratedType? calleeType;
     if (targetType != null &&
         targetType.type is FunctionType &&
-        propertyName.name == 'call') {
+        propertyName == 'call') {
       // If `X` has a function type, then in the expression `X.call`, the
       // function being torn off is `X` itself, so the callee type is simply the
       // non-nullable counterpart to the type of `X`.
@@ -3148,7 +3179,7 @@
       // Dynamic dispatch.
       return _makeNullableDynamicType(node);
     }
-    if (propertyName.inSetterContext()) {
+    if (inSetterContext) {
       if (isNullAware) {
         _conditionalNodes[node] = targetType!.node;
       }