Promote parameter of == to nullable when invoked.
This is required because otherwise
Object() == null
requires the type signature of Object.operator==(Object o) to be
changed to Object.operator==(Object? o). Which I don't think is the
behavior we want.
Confirmation that this CL is correct has been sent to the language
team. I recommend we land, which will unblock my subtyping CL, and
roll back/readdress later if need be.
Change-Id: I498f9870e7128b2cac3012fff0cb1ab50fcc8df7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/104344
Commit-Queue: Mike Fairhurst <mfairhurst@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 87d1892..3791919 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -420,9 +420,10 @@
_checkForUseOfVoidResult(node.rightOperand);
_checkForNullableDereference(node.leftOperand);
_checkForNullableDereference(node.rightOperand);
- } else if (type != TokenType.EQ_EQ &&
- type != TokenType.BANG_EQ &&
- type != TokenType.QUESTION_QUESTION) {
+ } else if (type == TokenType.EQ_EQ || type == TokenType.BANG_EQ) {
+ _checkForArgumentTypeNotAssignableForArgument(node.rightOperand,
+ promoteParameterToNullable: true);
+ } else if (type != TokenType.QUESTION_QUESTION) {
_checkForArgumentTypeNotAssignableForArgument(node.rightOperand);
_checkForNullableDereference(node.leftOperand);
} else {
@@ -2264,13 +2265,17 @@
*
* See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE].
*/
- void _checkForArgumentTypeNotAssignableForArgument(Expression argument) {
+ void _checkForArgumentTypeNotAssignableForArgument(Expression argument,
+ {bool promoteParameterToNullable = false}) {
if (argument == null) {
return;
}
ParameterElement staticParameterElement = argument.staticParameterElement;
DartType staticParameterType = staticParameterElement?.type;
+ if (promoteParameterToNullable && staticParameterType != null) {
+ staticParameterType = _typeSystem.makeNullable(staticParameterType);
+ }
_checkForArgumentTypeNotAssignableWithExpectedTypes(argument,
staticParameterType, StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE);
}
diff --git a/tests/language_2/nnbd/static_errors/equals_parameter_made_nullable_at_invoke_test.dart b/tests/language_2/nnbd/static_errors/equals_parameter_made_nullable_at_invoke_test.dart
new file mode 100644
index 0000000..35e756e
--- /dev/null
+++ b/tests/language_2/nnbd/static_errors/equals_parameter_made_nullable_at_invoke_test.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+import 'dart:async';
+
+// SharedOptions=--enable-experiment=non-nullable
+
+// Test that Object.operator==(Object o) is the signature of ==, but that we can
+// still compare nullable values to Object.
+//
+// This is because a == b is essentially sugar for
+//
+// ```
+// identical(a, null) || identical (b, null)
+// ? identical(a, b)
+// : a.operator==(b);
+// ```
+//
+// It should not be required to handle `null` inside the implementation of
+// operator==, but it should not be an error to "assign null" to the parameter
+// of the comparison operator.
+main() {
+ Object o;
+ // Valid comparison.
+ o == null;
+
+ // Caveat: it is NOT that the argument is promoted to non-null. Otherwise,
+ // types which we can't cleanly promote, such as FutureOr<int?>, would not be
+ // assignable in comparisons.
+ FutureOr<int?> foInt;
+
+ // Valid comparison.
+ o == foInt;
+}
+
+class C {
+ // Valid override
+ @override
+ bool operator==(Object other) => identical(other, this);
+}