Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 4121cf3

Browse files
bwilkersoncommit-bot@chromium.org
authored andcommitted
Add a quick fix to add a null check operator
Change-Id: Ie85ab236015057c2cb0255cc1548605e5b01e908 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/156783 Commit-Queue: Brian Wilkerson <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]>
1 parent 28c20c0 commit 4121cf3

File tree

5 files changed

+193
-0
lines changed

5 files changed

+193
-0
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright (c) 2020, 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:analysis_server/src/services/correction/dart/abstract_producer.dart';
6+
import 'package:analysis_server/src/services/correction/fix.dart';
7+
import 'package:analyzer/dart/analysis/features.dart';
8+
import 'package:analyzer/dart/ast/ast.dart';
9+
import 'package:analyzer/dart/ast/precedence.dart';
10+
import 'package:analyzer/dart/element/type.dart';
11+
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
12+
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
13+
14+
class AddNullCheck extends CorrectionProducer {
15+
@override
16+
FixKind get fixKind => DartFixKind.ADD_NULL_CHECK;
17+
18+
@override
19+
Future<void> compute(ChangeBuilder builder) async {
20+
if (!unit.featureSet.isEnabled(Feature.non_nullable)) {
21+
// Don't suggest a feature that isn't supported.
22+
return;
23+
}
24+
Expression target;
25+
if (coveredNode is Expression) {
26+
target = coveredNode;
27+
} else {
28+
return;
29+
}
30+
31+
var fromType = target.staticType;
32+
if (fromType == typeProvider.nullType) {
33+
// Adding a null check after an explicit `null` is pointless.
34+
return;
35+
}
36+
DartType toType;
37+
var parent = target.parent;
38+
if (parent is AssignmentExpression && target == parent.rightHandSide) {
39+
toType = parent.leftHandSide.staticType;
40+
} else if (parent is VariableDeclaration && target == parent.initializer) {
41+
toType = parent.declaredElement.type;
42+
} else if (parent is ArgumentList) {
43+
toType = target.staticParameterElement.type;
44+
} else {
45+
return;
46+
}
47+
if (!typeSystem.isAssignableTo(
48+
toType, typeSystem.promoteToNonNull(fromType))) {
49+
// The reason that `fromType` can't be assigned to `toType` is more than
50+
// just because it's nullable, in which case a null check won't fix the
51+
// problem.
52+
return;
53+
}
54+
var needsParentheses = target.precedence < Precedence.postfix;
55+
await builder.addDartFileEdit(file, (builder) {
56+
if (needsParentheses) {
57+
builder.addSimpleInsertion(target.offset, '(');
58+
}
59+
builder.addInsertion(target.end, (builder) {
60+
if (needsParentheses) {
61+
builder.write(')');
62+
}
63+
builder.write('!');
64+
});
65+
});
66+
}
67+
68+
/// Return an instance of this class. Used as a tear-off in `FixProcessor`.
69+
static AddNullCheck newInstance() => AddNullCheck();
70+
}

pkg/analysis_server/lib/src/services/correction/fix.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ class DartFixKind {
176176
"Add required argument '{0}'");
177177
static const ADD_NE_NULL = FixKind('dart.fix.add.neNull', 50, 'Add != null',
178178
appliedTogetherMessage: 'Add != null everywhere in file');
179+
static const ADD_NULL_CHECK =
180+
FixKind('dart.fix.add.nullCheck', 50, 'Add a null check (!)');
179181
static const ADD_OVERRIDE =
180182
FixKind('dart.fix.add.override', 50, "Add '@override' annotation");
181183
static const ADD_REQUIRED =

pkg/analysis_server/lib/src/services/correction/fix_internal.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import 'package:analysis_server/src/services/correction/dart/add_missing_paramet
2121
import 'package:analysis_server/src/services/correction/dart/add_missing_parameter_named.dart';
2222
import 'package:analysis_server/src/services/correction/dart/add_missing_required_argument.dart';
2323
import 'package:analysis_server/src/services/correction/dart/add_ne_null.dart';
24+
import 'package:analysis_server/src/services/correction/dart/add_null_check.dart';
2425
import 'package:analysis_server/src/services/correction/dart/add_override.dart';
2526
import 'package:analysis_server/src/services/correction/dart/add_required.dart';
2627
import 'package:analysis_server/src/services/correction/dart/add_required_keyword.dart';
@@ -591,6 +592,7 @@ class FixProcessor extends BaseProcessor {
591592
MakeVariableNotFinal.newInstance,
592593
],
593594
CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE: [
595+
AddNullCheck.newInstance,
594596
WrapInText.newInstance,
595597
],
596598
CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT: [
@@ -673,6 +675,7 @@ class FixProcessor extends BaseProcessor {
673675
],
674676
CompileTimeErrorCode.INVALID_ASSIGNMENT: [
675677
AddExplicitCast.newInstance,
678+
AddNullCheck.newInstance,
676679
ChangeTypeAnnotation.newInstance,
677680
],
678681
CompileTimeErrorCode.INVOCATION_OF_NON_FUNCTION_EXPRESSION: [
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Copyright (c) 2020, 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:analysis_server/src/services/correction/fix.dart';
6+
import 'package:analyzer/src/dart/analysis/experiments.dart';
7+
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
8+
import 'package:test_reflective_loader/test_reflective_loader.dart';
9+
10+
import 'fix_processor.dart';
11+
12+
void main() {
13+
defineReflectiveSuite(() {
14+
defineReflectiveTests(AddNullCheckTest);
15+
});
16+
}
17+
18+
@reflectiveTest
19+
class AddNullCheckTest extends FixProcessorTest {
20+
@override
21+
List<String> get experiments => [EnableString.non_nullable];
22+
23+
@override
24+
FixKind get kind => DartFixKind.ADD_NULL_CHECK;
25+
26+
Future<void> test_argument() async {
27+
await resolveTestUnit('''
28+
void f(int x) {}
29+
void g(int? y) {
30+
f(y);
31+
}
32+
''');
33+
await assertHasFix('''
34+
void f(int x) {}
35+
void g(int? y) {
36+
f(y!);
37+
}
38+
''');
39+
}
40+
41+
Future<void> test_argument_differByMoreThanNullability() async {
42+
await resolveTestUnit('''
43+
void f(int x) {}
44+
void g(String y) {
45+
f(y);
46+
}
47+
''');
48+
await assertNoFix();
49+
}
50+
51+
Future<void> test_assignment() async {
52+
await resolveTestUnit('''
53+
void f(int x, int? y) {
54+
x = y;
55+
}
56+
''');
57+
await assertHasFix('''
58+
void f(int x, int? y) {
59+
x = y!;
60+
}
61+
''');
62+
}
63+
64+
Future<void> test_assignment_differByMoreThanNullability() async {
65+
await resolveTestUnit('''
66+
void f(int x, String y) {
67+
x = y;
68+
}
69+
''');
70+
await assertNoFix();
71+
}
72+
73+
Future<void> test_assignment_needsParens() async {
74+
await resolveTestUnit('''
75+
void f(A x) {
76+
x = x + x;
77+
}
78+
class A {
79+
A? operator +(A a) => null;
80+
}
81+
''');
82+
await assertHasFix('''
83+
void f(A x) {
84+
x = (x + x)!;
85+
}
86+
class A {
87+
A? operator +(A a) => null;
88+
}
89+
''');
90+
}
91+
92+
Future<void> test_initializer() async {
93+
await resolveTestUnit('''
94+
void f(int? x) {
95+
int y = x;
96+
print(y);
97+
}
98+
''');
99+
await assertHasFix('''
100+
void f(int? x) {
101+
int y = x!;
102+
print(y);
103+
}
104+
''');
105+
}
106+
107+
Future<void> test_initializer_differByMoreThanNullability() async {
108+
await resolveTestUnit('''
109+
void f(String x) {
110+
int y = x;
111+
print(y);
112+
}
113+
''');
114+
await assertNoFix();
115+
}
116+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import 'add_missing_parameter_required_test.dart'
2323
import 'add_missing_required_argument_test.dart'
2424
as add_missing_required_argument;
2525
import 'add_ne_null_test.dart' as add_ne_null;
26+
import 'add_null_check_test.dart' as add_null_check;
2627
import 'add_override_test.dart' as add_override;
2728
import 'add_required_test.dart' as add_required;
2829
import 'add_return_type_test.dart' as add_return_type;
@@ -181,6 +182,7 @@ void main() {
181182
add_missing_parameter_required.main();
182183
add_missing_required_argument.main();
183184
add_ne_null.main();
185+
add_null_check.main();
184186
add_override.main();
185187
add_required.main();
186188
add_return_type.main();

0 commit comments

Comments
 (0)