Skip to content

Commit aad9e57

Browse files
committed
Revert "Revert "[analyzer] new warning for nullable '==' parameter type""
This reverts commit 885457e. [analyzer] new warning for nullable '==' parameter type This rule checks that a parameter to an `operator ==` implementation has a non-nullable type. I intentionally did not enforce, in this rule, that the parameter is exactly `Object`. It is legal to narrow the parameter type to a different non-nullable type, like `int`. I can't imagine doing it, but it seems to be unrelated to whether the type should be nullable or not. Fixes https://github.com/dart-lang/linter/issues/3441 Replaces dart-archive/linter#3923 Change-Id: Ic0be2bfebaf59b0336e9a3a58e5b7f5359eb8646 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/291042 Reviewed-by: Brian Wilkerson <[email protected]> Reviewed-by: Stephen Adams <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]>
1 parent 38f6c82 commit aad9e57

File tree

14 files changed

+177
-20
lines changed

14 files changed

+177
-20
lines changed

pkg/_fe_analyzer_shared/lib/src/parser/identifier_context_impl.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ class ExtensionShowHideElementIdentifierContext extends IdentifierContext {
449449
}
450450

451451
@override
452-
bool operator ==(dynamic other) {
452+
bool operator ==(Object other) {
453453
return other is ExtensionShowHideElementIdentifierContext &&
454454
_kind == other._kind;
455455
}

pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3699,6 +3699,11 @@ WarningCode.NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR_USING_NEW:
36993699
status: needsFix
37003700
notes: |-
37013701
The fix is to replace `new` with `const`.
3702+
WarningCode.NON_NULLABLE_EQUALS_PARAMETER:
3703+
status: needsFix
3704+
notes: |-
3705+
The fix is to remove the question mark if the parameter has one (if the
3706+
written type is an alias, it may not).
37023707
WarningCode.NULLABLE_TYPE_IN_CATCH_CLAUSE:
37033708
status: hasFix
37043709
WarningCode.NULL_ARGUMENT_TO_NON_NULL_TYPE:

pkg/analysis_server/test/src/services/correction/fix/remove_comparison_test.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,7 @@ class Person {
688688
final String name = '';
689689
690690
@override
691-
operator ==(Object? other) =>
691+
operator ==(Object other) =>
692692
other != null &&
693693
other is Person &&
694694
name == other.name;
@@ -699,7 +699,7 @@ class Person {
699699
final String name = '';
700700
701701
@override
702-
operator ==(Object? other) =>
702+
operator ==(Object other) =>
703703
other is Person &&
704704
name == other.name;
705705
}
@@ -712,7 +712,7 @@ class Person {
712712
final String name = '';
713713
714714
@override
715-
operator ==(Object? other) {
715+
operator ==(Object other) {
716716
return other != null &&
717717
other is Person &&
718718
name == other.name;
@@ -724,7 +724,7 @@ class Person {
724724
final String name = '';
725725
726726
@override
727-
operator ==(Object? other) {
727+
operator ==(Object other) {
728728
return other is Person &&
729729
name == other.name;
730730
}
@@ -738,7 +738,7 @@ class Person {
738738
final String name = '';
739739
740740
@override
741-
operator ==(Object? other) {
741+
operator ==(Object other) {
742742
if (other is! Person) return false;
743743
other ??= Person();
744744
return other.name == name;

pkg/analysis_server/tool/code_completion/metrics_util.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ class Place {
458458
int get rank => _numerator;
459459

460460
@override
461-
bool operator ==(dynamic other) =>
461+
bool operator ==(Object other) =>
462462
other is Place &&
463463
_numerator == other._numerator &&
464464
_denominator == other._denominator;

pkg/analyzer/lib/src/error/best_practices_verifier.dart

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor<void> {
593593
_checkForMissingReturn(node.body, node);
594594
_mustCallSuperVerifier.checkMethodDeclaration(node);
595595
_checkForUnnecessaryNoSuchMethod(node);
596+
_checkForNullableEqualsParameterType(node);
596597

597598
var name = Name(_currentLibrary.source.uri, element.name);
598599
var elementIsOverride = element is ClassMemberElement &&
@@ -1262,6 +1263,44 @@ class BestPracticesVerifier extends RecursiveAstVisitor<void> {
12621263
}
12631264
}
12641265

1266+
void _checkForNullableEqualsParameterType(MethodDeclaration node) {
1267+
if (!_typeSystem.isNonNullableByDefault) {
1268+
// Cannot specify non-nullable types before null safety.
1269+
return;
1270+
}
1271+
1272+
if (node.name.type != TokenType.EQ_EQ) {
1273+
return;
1274+
}
1275+
1276+
var parameters = node.parameters;
1277+
if (parameters == null) {
1278+
return;
1279+
}
1280+
1281+
if (parameters.parameters.length != 1) {
1282+
return;
1283+
}
1284+
1285+
var parameter = parameters.parameters.first;
1286+
var parameterElement = parameter.declaredElement;
1287+
if (parameterElement == null) {
1288+
return;
1289+
}
1290+
1291+
var type = parameterElement.type;
1292+
if (!type.isDartCoreObject && type is! DynamicType) {
1293+
// There is no legal way to define a nullable parameter type, which is not
1294+
// `dynamic` or `Object?`, so avoid double reporting here.
1295+
return;
1296+
}
1297+
1298+
if (_typeSystem.isNullable(parameterElement.type)) {
1299+
_errorReporter.reportErrorForToken(
1300+
WarningCode.NON_NULLABLE_EQUALS_PARAMETER, node.name);
1301+
}
1302+
}
1303+
12651304
void _checkForNullableTypeInCatchClause(CatchClause node) {
12661305
if (!_isNonNullableByDefault) {
12671306
return;

pkg/analyzer/lib/src/error/codes.g.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6894,6 +6894,13 @@ class WarningCode extends AnalyzerErrorCode {
68946894
uniqueName: 'NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR_USING_NEW',
68956895
);
68966896

6897+
/// No parameters.
6898+
static const WarningCode NON_NULLABLE_EQUALS_PARAMETER = WarningCode(
6899+
'NON_NULLABLE_EQUALS_PARAMETER',
6900+
"The parameter type of '==' operators should be non-nullable.",
6901+
correctionMessage: "Try using a non-nullable type.",
6902+
);
6903+
68976904
/// No parameters.
68986905
static const WarningCode NULLABLE_TYPE_IN_CATCH_CLAUSE = WarningCode(
68996906
'NULLABLE_TYPE_IN_CATCH_CLAUSE',

pkg/analyzer/lib/src/error/error_code_values.g.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,7 @@ const List<ErrorCode> errorCodeValues = [
10381038
WarningCode.MUST_CALL_SUPER,
10391039
WarningCode.NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR,
10401040
WarningCode.NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR_USING_NEW,
1041+
WarningCode.NON_NULLABLE_EQUALS_PARAMETER,
10411042
WarningCode.NULLABLE_TYPE_IN_CATCH_CLAUSE,
10421043
WarningCode.NULL_ARGUMENT_TO_NON_NULL_TYPE,
10431044
WarningCode.NULL_CHECK_ALWAYS_FAILS,

pkg/analyzer/messages.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6906,7 +6906,7 @@ CompileTimeErrorCode:
69066906

69076907
```dart
69086908
mixin M on Enum {
6909-
bool operator [!==!](Object? other) => false;
6909+
bool operator [!==!](Object other) => false;
69106910
}
69116911
```
69126912

@@ -24835,6 +24835,11 @@ WarningCode:
2483524835

2483624836
void f() => const C();
2483724837
```
24838+
NON_NULLABLE_EQUALS_PARAMETER:
24839+
problemMessage: "The parameter type of '==' operators should be non-nullable."
24840+
correctionMessage: Try using a non-nullable type.
24841+
hasPublishedDocs: false
24842+
comment: No parameters.
2483824843
NULLABLE_TYPE_IN_CATCH_CLAUSE:
2483924844
problemMessage: "A potentially nullable type can't be used in an 'on' clause because it isn't valid to throw a nullable expression."
2484024845
correctionMessage: Try using a non-nullable type.

pkg/analyzer/test/src/diagnostics/extension_declares_member_of_object_test.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,18 @@ extension E on String {
2828
test_instance_sameKind() async {
2929
await assertErrorsInCode('''
3030
extension E on String {
31-
bool operator==(_) => false;
31+
bool operator==(Object _) => false;
3232
int get hashCode => 0;
3333
String toString() => '';
3434
dynamic get runtimeType => null;
3535
dynamic noSuchMethod(_) => null;
3636
}
3737
''', [
3838
error(CompileTimeErrorCode.EXTENSION_DECLARES_MEMBER_OF_OBJECT, 39, 2),
39-
error(CompileTimeErrorCode.EXTENSION_DECLARES_MEMBER_OF_OBJECT, 65, 8),
40-
error(CompileTimeErrorCode.EXTENSION_DECLARES_MEMBER_OF_OBJECT, 89, 8),
41-
error(CompileTimeErrorCode.EXTENSION_DECLARES_MEMBER_OF_OBJECT, 121, 11),
42-
error(CompileTimeErrorCode.EXTENSION_DECLARES_MEMBER_OF_OBJECT, 152, 12),
39+
error(CompileTimeErrorCode.EXTENSION_DECLARES_MEMBER_OF_OBJECT, 72, 8),
40+
error(CompileTimeErrorCode.EXTENSION_DECLARES_MEMBER_OF_OBJECT, 96, 8),
41+
error(CompileTimeErrorCode.EXTENSION_DECLARES_MEMBER_OF_OBJECT, 128, 11),
42+
error(CompileTimeErrorCode.EXTENSION_DECLARES_MEMBER_OF_OBJECT, 159, 12),
4343
]);
4444
}
4545

pkg/analyzer/test/src/diagnostics/extension_type_declares_member_of_object_test.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ extension type E(int it) {
2929
test_instance_sameKind() async {
3030
await assertErrorsInCode('''
3131
extension type E(int it) {
32-
bool operator==(_) => false;
32+
bool operator==(Object _) => false;
3333
int get hashCode => 0;
3434
String toString() => '';
3535
dynamic get runtimeType => null;
@@ -39,12 +39,12 @@ extension type E(int it) {
3939
error(
4040
CompileTimeErrorCode.EXTENSION_TYPE_DECLARES_MEMBER_OF_OBJECT, 42, 2),
4141
error(
42-
CompileTimeErrorCode.EXTENSION_TYPE_DECLARES_MEMBER_OF_OBJECT, 68, 8),
42+
CompileTimeErrorCode.EXTENSION_TYPE_DECLARES_MEMBER_OF_OBJECT, 75, 8),
4343
error(
44-
CompileTimeErrorCode.EXTENSION_TYPE_DECLARES_MEMBER_OF_OBJECT, 92, 8),
45-
error(CompileTimeErrorCode.EXTENSION_TYPE_DECLARES_MEMBER_OF_OBJECT, 124,
44+
CompileTimeErrorCode.EXTENSION_TYPE_DECLARES_MEMBER_OF_OBJECT, 99, 8),
45+
error(CompileTimeErrorCode.EXTENSION_TYPE_DECLARES_MEMBER_OF_OBJECT, 131,
4646
11),
47-
error(CompileTimeErrorCode.EXTENSION_TYPE_DECLARES_MEMBER_OF_OBJECT, 155,
47+
error(CompileTimeErrorCode.EXTENSION_TYPE_DECLARES_MEMBER_OF_OBJECT, 162,
4848
12),
4949
]);
5050
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/src/error/codes.dart';
6+
import 'package:analyzer/src/utilities/legacy.dart';
7+
import 'package:test_reflective_loader/test_reflective_loader.dart';
8+
9+
import '../dart/resolution/context_collection_resolution.dart';
10+
11+
main() {
12+
defineReflectiveSuite(() {
13+
defineReflectiveTests(NonNullableEqualsParameterTest);
14+
});
15+
}
16+
17+
@reflectiveTest
18+
class NonNullableEqualsParameterTest extends PubPackageResolutionTest {
19+
test_dynamic() async {
20+
await assertErrorsInCode(r'''
21+
class C {
22+
@override
23+
bool operator ==(dynamic other) => false;
24+
}
25+
''', [
26+
error(WarningCode.NON_NULLABLE_EQUALS_PARAMETER, 38, 2),
27+
]);
28+
}
29+
30+
test_inheritedDynamic() async {
31+
await assertErrorsInCode(r'''
32+
class C {
33+
@override
34+
bool operator ==(dynamic other) => false;
35+
}
36+
class D extends C {
37+
@override
38+
bool operator ==(other) => false;
39+
}
40+
''', [
41+
error(WarningCode.NON_NULLABLE_EQUALS_PARAMETER, 38, 2),
42+
error(WarningCode.NON_NULLABLE_EQUALS_PARAMETER, 116, 2),
43+
]);
44+
}
45+
46+
test_inheritedFromObject() async {
47+
await assertNoErrorsInCode(r'''
48+
class C {
49+
@override
50+
bool operator ==(other) => false;
51+
}
52+
''');
53+
}
54+
55+
test_inheritedFromObject_preNullSafe() async {
56+
try {
57+
noSoundNullSafety = false;
58+
await assertNoErrorsInCode(r'''
59+
// @dart=2.9
60+
class C {
61+
@override
62+
bool operator ==(other) => false;
63+
}
64+
''');
65+
} finally {
66+
noSoundNullSafety = true;
67+
}
68+
}
69+
70+
test_int() async {
71+
await assertNoErrorsInCode(r'''
72+
class C {
73+
@override
74+
bool operator ==(covariant int other) => false;
75+
}
76+
''');
77+
}
78+
79+
test_nullableObject() async {
80+
await assertErrorsInCode(r'''
81+
class C {
82+
@override
83+
bool operator ==(Object? other) => false;
84+
}
85+
''', [
86+
error(WarningCode.NON_NULLABLE_EQUALS_PARAMETER, 38, 2),
87+
]);
88+
}
89+
90+
test_object() async {
91+
await assertNoErrorsInCode(r'''
92+
class C {
93+
@override
94+
bool operator ==(Object other) => false;
95+
}
96+
''');
97+
}
98+
}

pkg/analyzer/test/src/diagnostics/test_all.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,7 @@ import 'non_generative_implicit_constructor_test.dart'
637637
import 'non_native_function_type_argument_to_pointer_test.dart'
638638
as non_native_function_type_argument_to_pointer;
639639
import 'non_null_opt_out_test.dart' as non_null_opt_out;
640+
import 'non_nullable_equals_parameter_test.dart' as non_null_equals_parameters;
640641
import 'non_positive_array_dimension_test.dart' as non_positive_array_dimension;
641642
import 'non_sized_type_argument_test.dart' as non_sized_type_argument;
642643
import 'non_type_as_type_argument_test.dart' as non_type_as_type_argument;
@@ -1327,6 +1328,7 @@ main() {
13271328
non_generative_implicit_constructor.main();
13281329
non_native_function_type_argument_to_pointer.main();
13291330
non_null_opt_out.main();
1331+
non_null_equals_parameters.main();
13301332
non_positive_array_dimension.main();
13311333
non_sized_type_argument.main();
13321334
non_type_as_type_argument.main();

pkg/analyzer/tool/diagnostics/diagnostics.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8189,7 +8189,7 @@ has `Enum` in the `on` clause, declares an explicit operator named `==`:
81898189

81908190
{% prettify dart tag=pre+code %}
81918191
mixin M on Enum {
8192-
bool operator [!==!](Object? other) => false;
8192+
bool operator [!==!](Object other) => false;
81938193
}
81948194
{% endprettify %}
81958195

pkg/compiler/lib/src/inferrer/locals_handler.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ class ArgumentsTypes extends IterableMixin<TypeInformation> {
254254
String toString() => "{ positional = $positional, named = $named }";
255255

256256
@override
257-
bool operator ==(Object? other) {
257+
bool operator ==(Object other) {
258258
if (other is! ArgumentsTypes) return false;
259259
if (positional.length != other.positional.length) return false;
260260
if (named.length != other.named.length) return false;

0 commit comments

Comments
 (0)