Skip to content

Commit ad7374f

Browse files
pqcommit-bot@chromium.org
authored andcommitted
assist to insert null checks
(still WIP) Change-Id: Icca46e4a9fbca43f00151d99cb24935e18c97294 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/117589 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Phil Quitslund <[email protected]>
1 parent 51d6145 commit ad7374f

File tree

4 files changed

+97
-0
lines changed

4 files changed

+97
-0
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ class DartAssistContextImpl implements DartAssistContext {
3131
* An enumeration of possible assist kinds.
3232
*/
3333
class DartAssistKind {
34+
static const ADD_NOT_NULL_ASSERT = const AssistKind(
35+
'dart.assist.addNotNullAssert', 30, "Add a not-null assertion");
3436
static const ADD_TYPE_ANNOTATION = const AssistKind(
3537
'dart.assist.addTypeAnnotation', 30, "Add type annotation");
3638
static const ASSIGN_TO_LOCAL_VARIABLE = const AssistKind(

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'dart:async';
66
import 'dart:collection';
7+
import 'dart:math';
78

89
import 'package:analysis_server/plugin/edit/assist/assist_core.dart';
910
import 'package:analysis_server/plugin/edit/assist/assist_dart.dart';
@@ -61,6 +62,7 @@ class AssistProcessor extends BaseProcessor {
6162
)) {
6263
await _addProposals_addTypeAnnotation();
6364
}
65+
await _addProposal_addNotNullAssert();
6466
await _addProposal_assignToLocalVariable();
6567
await _addProposal_convertClassToMixin();
6668
await _addProposal_convertDocumentationIntoBlock();
@@ -225,6 +227,42 @@ class AssistProcessor extends BaseProcessor {
225227
assists.add(new Assist(kind, change));
226228
}
227229

230+
Future<void> _addProposal_addNotNullAssert() async {
231+
if (node is SimpleIdentifier) {
232+
if (node.parent is FormalParameter) {
233+
final exp = node.parent.thisOrAncestorMatching(
234+
(node) => node is FunctionExpression || node is MethodDeclaration);
235+
var body;
236+
if (exp is FunctionExpression) {
237+
body = exp.body;
238+
} else if (exp is MethodDeclaration) {
239+
body = exp.body;
240+
}
241+
if (body is BlockFunctionBody) {
242+
final changeBuilder = _newDartChangeBuilder();
243+
await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
244+
final id = (node as SimpleIdentifier).name;
245+
final prefix = utils.getNodePrefix(exp);
246+
final indent = utils.getIndent(1);
247+
// todo (pq): follow-ups:
248+
// 1. if the end token is on the same line as the body
249+
// we should add an `eol` before the assert as well.
250+
// 2. also, consider asking the block for the list of statements and
251+
// adding the statement to the beginning of the list, special casing
252+
// when there are no statements (or when there's a single statement
253+
// and the whole block is on the same line).
254+
final int offset = min(utils.getLineNext(body.beginToken.offset),
255+
body.endToken.offset);
256+
builder.addSimpleInsertion(
257+
offset, '$prefix${indent}assert($id != null);$eol');
258+
_addAssistFromBuilder(
259+
changeBuilder, DartAssistKind.ADD_NOT_NULL_ASSERT);
260+
});
261+
}
262+
}
263+
}
264+
}
265+
228266
Future<void> _addProposal_assignToLocalVariable() async {
229267
// prepare enclosing ExpressionStatement
230268
ExpressionStatement expressionStatement;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) 2019, 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/assist.dart';
6+
import 'package:analyzer_plugin/utilities/assist/assist.dart';
7+
import 'package:test_reflective_loader/test_reflective_loader.dart';
8+
9+
import 'assist_processor.dart';
10+
11+
main() {
12+
defineReflectiveSuite(() {
13+
defineReflectiveTests(AddNotNullAssert);
14+
});
15+
}
16+
17+
@reflectiveTest
18+
class AddNotNullAssert extends AssistProcessorTest {
19+
@override
20+
AssistKind get kind => DartAssistKind.ADD_NOT_NULL_ASSERT;
21+
22+
test_function_expressionBody_noAssert() async {
23+
await resolveTestUnit('''
24+
int double(int x) => x * 2;
25+
''');
26+
// todo (pq): support expression bodies.
27+
await assertNoAssistAt('x');
28+
}
29+
30+
test_function_noAssert() async {
31+
await resolveTestUnit('''
32+
foo(int x) {
33+
}
34+
''');
35+
await assertHasAssistAt('x', '''
36+
foo(int x) {
37+
assert(x != null);
38+
}
39+
''');
40+
}
41+
42+
test_method_noAssert() async {
43+
await resolveTestUnit('''
44+
class A {
45+
foo(int x) {
46+
}
47+
}''');
48+
await assertHasAssistAt('x', '''
49+
class A {
50+
foo(int x) {
51+
assert(x != null);
52+
}
53+
}''');
54+
}
55+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:test_reflective_loader/test_reflective_loader.dart';
66

7+
import 'add_not_null_assert.dart' as add_not_null_assert;
78
import 'add_type_annotation_test.dart' as add_type_annotation;
89
import 'assign_to_local_variable_test.dart' as assign_to_local_variable;
910
import 'convert_class_to_mixin_test.dart' as convert_class_to_mixin;
@@ -82,6 +83,7 @@ import 'use_curly_braces_test.dart' as use_curly_braces;
8283

8384
main() {
8485
defineReflectiveSuite(() {
86+
add_not_null_assert.main();
8587
add_type_annotation.main();
8688
assign_to_local_variable.main();
8789
convert_class_to_mixin.main();

0 commit comments

Comments
 (0)