Issue 43268. Report warnings for Ext(c)?.foo() when 'c' is non-nullable.

Bug: https://github.com/dart-lang/sdk/issues/43268
Change-Id: I8b36e3e112eb91dc5d56e98f657f2c77bf761ada
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/161143
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index c37e6c6..54211be 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -834,13 +834,10 @@
     checkIndexExpression(node.auxiliaryElements?.staticElement);
 
     if (node.isNullAware) {
-      var target = node.realTarget;
-      if (_isExpressionWithType(target)) {
-        _checkForUnnecessaryNullAware(
-          target,
-          node.question ?? node.period ?? node.leftBracket,
-        );
-      }
+      _checkForUnnecessaryNullAware(
+        node.realTarget,
+        node.question ?? node.period ?? node.leftBracket,
+      );
     }
     super.visitIndexExpression(node);
   }
@@ -934,9 +931,7 @@
       _checkForStaticAccessToInstanceMember(typeReference, methodName);
       _checkForInstanceAccessToStaticMember(
           typeReference, node.target, methodName);
-      if (_isExpressionWithType(target)) {
-        _checkForUnnecessaryNullAware(target, node.operator);
-      }
+      _checkForUnnecessaryNullAware(target, node.operator);
     } else {
       _checkForUnqualifiedReferenceToNonLocalStaticMember(methodName);
     }
@@ -1039,9 +1034,7 @@
     _checkForStaticAccessToInstanceMember(typeReference, propertyName);
     _checkForInstanceAccessToStaticMember(
         typeReference, node.target, propertyName);
-    if (_isExpressionWithType(target)) {
-      _checkForUnnecessaryNullAware(target, node.operator);
-    }
+    _checkForUnnecessaryNullAware(target, node.operator);
 
     super.visitPropertyAccess(node);
   }
@@ -4364,33 +4357,37 @@
         var type = operator.type;
         if (type == TokenType.QUESTION_PERIOD) {
           var realTarget = target.realTarget;
-          if (_isExpressionWithType(realTarget)) {
-            return previousShortCircuitingOperator(realTarget) ?? operator;
-          }
+          return previousShortCircuitingOperator(realTarget) ?? operator;
         }
       } else if (target is IndexExpression) {
         if (target.question != null) {
           var realTarget = target.realTarget;
-          if (_isExpressionWithType(realTarget)) {
-            return previousShortCircuitingOperator(realTarget) ??
-                target.question;
-          }
+          return previousShortCircuitingOperator(realTarget) ?? target.question;
         }
       } else if (target is MethodInvocation) {
         var operator = target.operator;
         var type = operator?.type;
         if (type == TokenType.QUESTION_PERIOD) {
           var realTarget = target.realTarget;
-          if (_isExpressionWithType(realTarget)) {
-            return previousShortCircuitingOperator(realTarget) ?? operator;
-          }
-          return operator;
+          return previousShortCircuitingOperator(realTarget) ?? operator;
         }
       }
       return null;
     }
 
-    if (_typeSystem.isStrictlyNonNullable(target.staticType)) {
+    var targetType = target.staticType;
+    if (target is ExtensionOverride) {
+      var arguments = target.argumentList.arguments;
+      if (arguments.length == 1) {
+        targetType = arguments[0].staticType;
+      } else {
+        return;
+      }
+    } else if (targetType == null) {
+      return;
+    }
+
+    if (_typeSystem.isStrictlyNonNullable(targetType)) {
       if (errorCode == StaticWarningCode.INVALID_NULL_AWARE_OPERATOR) {
         var previousOperator = previousShortCircuitingOperator(target);
         if (previousOperator != null) {
@@ -5331,20 +5328,6 @@
     }
     return null;
   }
-
-  static bool _isExpressionWithType(Expression node) {
-    if (node is ExtensionOverride) {
-      return false;
-    }
-
-    // For `foo?.bar`, `foo` must be an identifier with a value.
-    if (node is Identifier) {
-      var element = node.staticElement;
-      return element is PropertyAccessorElement || element is VariableElement;
-    }
-
-    return true;
-  }
 }
 
 /// A record of the elements that will be declared in some scope (block), but
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart
index 3f27eb1..bce4df1 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart
@@ -89,6 +89,88 @@
 @reflectiveTest
 class InvalidNullAwareOperatorTest extends PubPackageResolutionTest
     with WithNullSafetyMixin {
+  test_extensionOverride_assignmentExpression_indexExpression() async {
+    await assertErrorsInCode('''
+extension E on int {
+  operator[]=(int index, bool _) {}
+}
+
+void f(int? a, int b) {
+  E(a)?[0] = true;
+  E(b)?[0] = true;
+}
+''', [
+      error(StaticWarningCode.INVALID_NULL_AWARE_OPERATOR, 109, 2),
+    ]);
+  }
+
+  test_extensionOverride_assignmentExpression_propertyAccess() async {
+    await assertErrorsInCode('''
+extension E on int {
+  set foo(bool _) {}
+}
+
+void f(int? a, int b) {
+  E(a)?.foo = true;
+  E(b)?.foo = true;
+}
+''', [
+      error(StaticWarningCode.INVALID_NULL_AWARE_OPERATOR, 95, 2),
+    ]);
+  }
+
+  test_extensionOverride_indexExpression() async {
+    await assertErrorsInCode('''
+extension E on int {
+  bool operator[](int index) => true;
+}
+
+void f(int? a, int b) {
+  E(a)?[0];
+  E(b)?[0];
+}
+''', [
+      error(StaticWarningCode.INVALID_NULL_AWARE_OPERATOR, 104, 2),
+    ]);
+    assertType(findNode.index('E(a)'), 'bool?');
+    assertType(findNode.index('E(b)'), 'bool?');
+  }
+
+  test_extensionOverride_methodInvocation() async {
+    await assertErrorsInCode('''
+extension E on int {
+  bool foo() => true;
+}
+
+void f(int? a, int b) {
+  E(a)?.foo();
+  E(b)?.foo();
+}
+''', [
+      error(StaticWarningCode.INVALID_NULL_AWARE_OPERATOR, 91, 2),
+    ]);
+
+    assertType(findNode.methodInvocation('E(a)'), 'bool?');
+    assertType(findNode.methodInvocation('E(b)'), 'bool?');
+  }
+
+  test_extensionOverride_propertyAccess() async {
+    await assertErrorsInCode('''
+extension E on int {
+  bool get foo => true;
+}
+
+void f(int? a, int b) {
+  E(a)?.foo;
+  E(b)?.foo;
+}
+''', [
+      error(StaticWarningCode.INVALID_NULL_AWARE_OPERATOR, 91, 2),
+    ]);
+    assertType(findNode.propertyAccess('E(a)'), 'bool?');
+    assertType(findNode.propertyAccess('E(b)'), 'bool?');
+  }
+
   test_getter_class() async {
     await assertNoErrorsInCode('''
 class C {
diff --git a/pkg/analyzer/test/src/diagnostics/unnecessary_non_null_assertion_test.dart b/pkg/analyzer/test/src/diagnostics/unnecessary_non_null_assertion_test.dart
index a72ca9d..3104f05 100644
--- a/pkg/analyzer/test/src/diagnostics/unnecessary_non_null_assertion_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unnecessary_non_null_assertion_test.dart
@@ -31,7 +31,33 @@
 ''');
   }
 
-  test_nonNull() async {
+  test_nonNull_function() async {
+    await assertErrorsInCode('''
+void g() {}
+
+void f() {
+  g!();
+}
+''', [
+      error(StaticWarningCode.UNNECESSARY_NON_NULL_ASSERTION, 27, 1),
+    ]);
+  }
+
+  test_nonNull_method() async {
+    await assertErrorsInCode('''
+class A {
+  static void foo() {}
+}
+
+void f() {
+  A.foo!();
+}
+''', [
+      error(StaticWarningCode.UNNECESSARY_NON_NULL_ASSERTION, 54, 1),
+    ]);
+  }
+
+  test_nonNull_parameter() async {
     await assertErrorsInCode('''
 f(int x) {
   x!;