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!;