Skip to content

Commit a629dbf

Browse files
bwilkersoncommit-bot@chromium.org
authored andcommitted
Add a fix to inline a typedef
I needed to fix AST nodes for parameters to know about the required token when computing the beginning token and child entities. Change-Id: Icb0fc27bc1e9f6650376f5870944fb8f592a5e8c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/138744 Commit-Queue: Brian Wilkerson <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]>
1 parent 0f28464 commit a629dbf

File tree

8 files changed

+343
-12
lines changed

8 files changed

+343
-12
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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:_fe_analyzer_shared/src/scanner/token.dart';
6+
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
7+
import 'package:analysis_server/src/services/correction/fix.dart';
8+
import 'package:analyzer/dart/ast/ast.dart';
9+
import 'package:analyzer/dart/ast/visitor.dart';
10+
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
11+
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
12+
import 'package:analyzer_plugin/utilities/range_factory.dart';
13+
14+
class InlineTypedef extends CorrectionProducer {
15+
String name;
16+
17+
@override
18+
List<Object> get fixArguments => [name];
19+
20+
@override
21+
FixKind get fixKind => DartFixKind.INLINE_TYPEDEF;
22+
23+
@override
24+
Future<void> compute(DartChangeBuilder builder) async {
25+
//
26+
// Extract the information needed to build the edit.
27+
//
28+
TypeAnnotation returnType;
29+
TypeParameterList typeParameters;
30+
List<FormalParameter> parameters;
31+
if (node is FunctionTypeAlias) {
32+
var typedef = node as FunctionTypeAlias;
33+
returnType = typedef.returnType;
34+
name = typedef.name.name;
35+
typeParameters = typedef.typeParameters;
36+
parameters = typedef.parameters.parameters;
37+
} else if (node is GenericTypeAlias) {
38+
var typedef = node as GenericTypeAlias;
39+
if (typedef.typeParameters != null) {
40+
return;
41+
}
42+
var functionType = typedef.functionType;
43+
returnType = functionType.returnType;
44+
name = typedef.name.name;
45+
typeParameters = functionType.typeParameters;
46+
parameters = functionType.parameters.parameters;
47+
} else {
48+
return;
49+
}
50+
// TODO(brianwilkerson) Handle parts.
51+
var finder = _ReferenceFinder(name);
52+
resolvedResult.unit.accept(finder);
53+
if (finder.count != 1) {
54+
return;
55+
}
56+
//
57+
// Build the edit.
58+
//
59+
await builder.addFileEdit(file, (DartFileEditBuilder builder) {
60+
builder.addDeletion(utils.getLinesRange(range.node(node)));
61+
builder.addReplacement(range.node(finder.reference),
62+
(DartEditBuilder builder) {
63+
if (returnType != null) {
64+
builder.write(utils.getNodeText(returnType));
65+
builder.write(' ');
66+
}
67+
builder.write('Function');
68+
if (typeParameters != null) {
69+
builder.write(utils.getNodeText(typeParameters));
70+
}
71+
String groupEnd;
72+
builder.write('(');
73+
for (int i = 0; i < parameters.length; i++) {
74+
var parameter = parameters[i];
75+
if (i > 0) {
76+
// This intentionally drops any trailing comma in order to improve
77+
// formatting.
78+
builder.write(', ');
79+
}
80+
if (parameter is DefaultFormalParameter) {
81+
if (groupEnd == null) {
82+
if (parameter.isNamed) {
83+
groupEnd = '}';
84+
builder.write('{');
85+
} else {
86+
groupEnd = ']';
87+
builder.write('[');
88+
}
89+
}
90+
parameter = (parameter as DefaultFormalParameter).parameter;
91+
}
92+
if (parameter is FunctionTypedFormalParameter) {
93+
builder.write(utils.getNodeText(parameter));
94+
} else if (parameter is SimpleFormalParameter) {
95+
if (parameter.metadata.isNotEmpty) {
96+
builder
97+
.write(utils.getRangeText(range.nodes(parameter.metadata)));
98+
}
99+
if (parameter.requiredKeyword != null) {
100+
builder.write('required ');
101+
}
102+
if (parameter.covariantKeyword != null) {
103+
builder.write('covariant ');
104+
}
105+
var keyword = parameter.keyword;
106+
if (keyword != null && keyword.type != Keyword.VAR) {
107+
builder.write(keyword.lexeme);
108+
}
109+
if (parameter.type == null) {
110+
builder.write('dynamic');
111+
} else {
112+
builder.write(utils.getNodeText(parameter.type));
113+
}
114+
if (parameter.isNamed) {
115+
builder.write(' ');
116+
builder.write(parameter.identifier.name);
117+
}
118+
}
119+
}
120+
if (groupEnd != null) {
121+
builder.write(groupEnd);
122+
}
123+
builder.write(')');
124+
});
125+
});
126+
}
127+
}
128+
129+
class _ReferenceFinder extends RecursiveAstVisitor {
130+
final String typeName;
131+
132+
TypeName reference;
133+
134+
int count = 0;
135+
136+
_ReferenceFinder(this.typeName);
137+
138+
@override
139+
void visitTypeName(TypeName node) {
140+
if (node.name.name == typeName) {
141+
reference ??= node;
142+
count++;
143+
}
144+
super.visitTypeName(node);
145+
}
146+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,8 @@ class DartFixKind {
268268
FixKind('IMPORT_LIBRARY_SHOW', 55, "Update library '{0}' import");
269269
static const INLINE_INVOCATION =
270270
FixKind('INLINE_INVOCATION', 30, "Inline invocation of '{0}'");
271+
static const INLINE_TYPEDEF =
272+
FixKind('INLINE_TYPEDEF', 30, "Inline the definition of '{0}'");
271273
static const INSERT_SEMICOLON = FixKind('INSERT_SEMICOLON', 50, "Insert ';'");
272274
static const MAKE_CLASS_ABSTRACT =
273275
FixKind('MAKE_CLASS_ABSTRACT', 50, "Make class '{0}' abstract");

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import 'package:analysis_server/src/services/correction/dart/convert_to_map_lite
1818
import 'package:analysis_server/src/services/correction/dart/convert_to_null_aware.dart';
1919
import 'package:analysis_server/src/services/correction/dart/convert_to_set_literal.dart';
2020
import 'package:analysis_server/src/services/correction/dart/convert_to_where_type.dart';
21+
import 'package:analysis_server/src/services/correction/dart/inline_typedef.dart';
2122
import 'package:analysis_server/src/services/correction/dart/remove_dead_if_null.dart';
2223
import 'package:analysis_server/src/services/correction/dart/remove_if_null_operator.dart';
2324
import 'package:analysis_server/src/services/correction/dart/replace_with_eight_digit_hex.dart';
@@ -4717,7 +4718,9 @@ class FixProcessor extends BaseProcessor {
47174718
await compute(RemoveDeadIfNull());
47184719
} else if (errorCode is LintCode) {
47194720
String name = errorCode.name;
4720-
if (name == LintNames.avoid_returning_null_for_future) {
4721+
if (name == LintNames.avoid_private_typedef_functions) {
4722+
await compute(InlineTypedef());
4723+
} else if (name == LintNames.avoid_returning_null_for_future) {
47214724
await compute(WrapInFuture());
47224725
} else if (name == LintNames.prefer_collection_literals) {
47234726
await compute(ConvertToListLiteral());

pkg/analysis_server/lib/src/services/linter/lint_names.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ class LintNames {
1919
static const String avoid_relative_lib_imports = 'avoid_relative_lib_imports';
2020
static const String avoid_return_types_on_setters =
2121
'avoid_return_types_on_setters';
22+
static const String avoid_private_typedef_functions =
23+
'avoid_private_typedef_functions';
2224
static const String avoid_returning_null_for_future =
2325
'avoid_returning_null_for_future';
2426
static const String avoid_types_on_closure_parameters =

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,17 @@ abstract class FixProcessorLintTest extends FixProcessorTest {
2727
/// The offset of the lint marker in the code being analyzed.
2828
int lintOffset = -1;
2929

30+
/// Return a list of the experiments that are to be enabled for tests in this
31+
/// class, or `null` if there are no experiments that should be enabled.
32+
List<String> get experiments => null;
33+
3034
/// Return the lint code being tested.
3135
String get lintCode;
3236

3337
@override
3438
void setUp() {
3539
super.setUp();
36-
createAnalysisOptionsFile(lints: [lintCode]);
40+
createAnalysisOptionsFile(experiments: experiments, lints: [lintCode]);
3741
}
3842

3943
/// Find the error that is to be fixed by computing the errors in the file,
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
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:analysis_server/src/services/linter/lint_names.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(InlineTypedefTest);
15+
defineReflectiveTests(InlineTypedefWithNNBDTest);
16+
});
17+
}
18+
19+
@reflectiveTest
20+
class InlineTypedefTest extends FixProcessorLintTest {
21+
@override
22+
FixKind get kind => DartFixKind.INLINE_TYPEDEF;
23+
24+
@override
25+
String get lintCode => LintNames.avoid_private_typedef_functions;
26+
27+
Future<void> test_generic_parameter_optionalNamed() async {
28+
await resolveTestUnit('''
29+
typedef _F = Function({int i});
30+
void g(_F f) {}
31+
''');
32+
await assertHasFix('''
33+
void g(Function({int i}) f) {}
34+
''');
35+
}
36+
37+
Future<void> test_generic_parameter_optionalPositional_withName() async {
38+
await resolveTestUnit('''
39+
typedef _F = Function([int i]);
40+
void g(_F f) {}
41+
''');
42+
await assertHasFix('''
43+
void g(Function([int]) f) {}
44+
''');
45+
}
46+
47+
Future<void> test_generic_parameter_optionalPositional_withoutName() async {
48+
await resolveTestUnit('''
49+
typedef _F = Function([int]);
50+
void g(_F f) {}
51+
''');
52+
await assertHasFix('''
53+
void g(Function([int]) f) {}
54+
''');
55+
}
56+
57+
Future<void> test_generic_parameter_requiredPositional_withName() async {
58+
await resolveTestUnit('''
59+
typedef _F = Function(int i);
60+
void g(_F f) {}
61+
''');
62+
await assertHasFix('''
63+
void g(Function(int) f) {}
64+
''');
65+
}
66+
67+
Future<void> test_generic_parameter_requiredPositional_withoutName() async {
68+
await resolveTestUnit('''
69+
typedef _F = Function(int);
70+
void g(_F f) {}
71+
''');
72+
await assertHasFix('''
73+
void g(Function(int) f) {}
74+
''');
75+
}
76+
77+
Future<void> test_generic_returnType() async {
78+
await resolveTestUnit('''
79+
typedef _F = void Function();
80+
void g(_F f) {}
81+
''');
82+
await assertHasFix('''
83+
void g(void Function() f) {}
84+
''');
85+
}
86+
87+
Future<void> test_generic_typeParameters() async {
88+
await resolveTestUnit('''
89+
typedef _F = Function<T>(T);
90+
void g(_F f) {}
91+
''');
92+
await assertHasFix('''
93+
void g(Function<T>(T) f) {}
94+
''');
95+
}
96+
97+
Future<void> test_nonGeneric_parameter_requiredPositional_typed() async {
98+
await resolveTestUnit('''
99+
typedef _F(int i);
100+
void g(_F f) {}
101+
''');
102+
await assertHasFix('''
103+
void g(Function(int) f) {}
104+
''');
105+
}
106+
107+
Future<void> test_nonGeneric_parameter_requiredPositional_untyped() async {
108+
await resolveTestUnit('''
109+
typedef _F(i);
110+
void g(_F f) {}
111+
''');
112+
await assertHasFix('''
113+
void g(Function(dynamic) f) {}
114+
''');
115+
}
116+
117+
Future<void> test_nonGeneric_returnType() async {
118+
await resolveTestUnit('''
119+
typedef void _F();
120+
void g(_F f) {}
121+
''');
122+
await assertHasFix('''
123+
void g(void Function() f) {}
124+
''');
125+
}
126+
127+
Future<void> test_nonGeneric_typeParameters() async {
128+
await resolveTestUnit('''
129+
typedef _F<T>(T t);
130+
void g(_F f) {}
131+
''');
132+
await assertHasFix('''
133+
void g(Function<T>(T) f) {}
134+
''');
135+
}
136+
}
137+
138+
@reflectiveTest
139+
class InlineTypedefWithNNBDTest extends InlineTypedefTest {
140+
@override
141+
List<String> get experiments => ['non-nullable'];
142+
143+
Future<void> test_generic_parameter_requiredNamed() async {
144+
await resolveTestUnit('''
145+
typedef _F = Function({required int i});
146+
void g(_F f) {}
147+
''');
148+
await assertHasFix('''
149+
void g(Function({required int i}) f) {}
150+
''');
151+
}
152+
153+
Future<void> test_nonGeneric_parameter_requiredNamed() async {
154+
await resolveTestUnit('''
155+
typedef _F({required int i});
156+
void g(_F f) {}
157+
''');
158+
await assertHasFix('''
159+
void g(Function({required int i}) f) {}
160+
''');
161+
}
162+
}

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
@@ -82,6 +82,7 @@ import 'import_library_project_test.dart' as import_library_project;
8282
import 'import_library_sdk_test.dart' as import_library_sdk;
8383
import 'import_library_show_test.dart' as import_library_show;
8484
import 'inline_invocation_test.dart' as inline_invocation;
85+
import 'inline_typedef_test.dart' as inline_typedef;
8586
import 'insert_semicolon_test.dart' as insert_semicolon;
8687
import 'make_class_abstract_test.dart' as make_class_abstract;
8788
import 'make_field_not_final_test.dart' as make_field_not_final;
@@ -221,6 +222,7 @@ void main() {
221222
import_library_sdk.main();
222223
import_library_show.main();
223224
inline_invocation.main();
225+
inline_typedef.main();
224226
insert_semicolon.main();
225227
make_class_abstract.main();
226228
make_field_not_final.main();

0 commit comments

Comments
 (0)