Add quick fixes for invalid return types for generator functions
Change-Id: I23a3befd37946c02aadc8c0ebdf75e60cf6767c2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/225426
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/replace_return_type_future.dart b/pkg/analysis_server/lib/src/services/correction/dart/replace_return_type_future.dart
index fb29e4f..9d3b8dd 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/replace_return_type_future.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/replace_return_type_future.dart
@@ -9,6 +9,12 @@
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
class ReplaceReturnTypeFuture extends CorrectionProducer {
+ /// The text for the type argument to 'Future'.
+ String _typeArgument = '';
+
+ @override
+ List<Object>? get fixArguments => [_typeArgument];
+
@override
FixKind get fixKind => DartFixKind.REPLACE_RETURN_TYPE_FUTURE;
@@ -19,6 +25,7 @@
if (typeAnnotation == null) {
return;
}
+ _typeArgument = utils.getNodeText(typeAnnotation);
await builder.addDartFileEdit(file, (builder) {
builder.replaceTypeWithFuture(typeAnnotation, typeProvider);
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/replace_return_type_iterable.dart b/pkg/analysis_server/lib/src/services/correction/dart/replace_return_type_iterable.dart
new file mode 100644
index 0000000..cc491aa
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/replace_return_type_iterable.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2020, 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 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ReplaceReturnTypeIterable extends CorrectionProducer {
+ /// The text for the type argument to 'Iterable'.
+ String _typeArgument = '';
+
+ @override
+ List<Object>? get fixArguments => [_typeArgument];
+
+ @override
+ FixKind get fixKind => DartFixKind.REPLACE_RETURN_TYPE_ITERABLE;
+
+ @override
+ Future<void> compute(ChangeBuilder builder) async {
+ // prepare the existing type
+ var typeAnnotation = node.thisOrAncestorOfType<TypeAnnotation>();
+ if (typeAnnotation == null) {
+ return;
+ }
+ var type = typeAnnotation.type;
+ if (type == null || type.isDynamic || type.isDartCoreIterable) {
+ return;
+ }
+ _typeArgument = utils.getNodeText(typeAnnotation);
+
+ await builder.addDartFileEdit(file, (builder) {
+ builder.addReplacement(range.node(typeAnnotation), (builder) {
+ builder.writeType(typeProvider.iterableType(type));
+ });
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
+ static ReplaceReturnTypeIterable newInstance() => ReplaceReturnTypeIterable();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/replace_return_type_stream.dart b/pkg/analysis_server/lib/src/services/correction/dart/replace_return_type_stream.dart
new file mode 100644
index 0000000..829ebe6
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/replace_return_type_stream.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2020, 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 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ReplaceReturnTypeStream extends CorrectionProducer {
+ /// The text for the type argument to 'Stream'.
+ String _typeArgument = '';
+
+ @override
+ List<Object>? get fixArguments => [_typeArgument];
+
+ @override
+ FixKind get fixKind => DartFixKind.REPLACE_RETURN_TYPE_STREAM;
+
+ @override
+ Future<void> compute(ChangeBuilder builder) async {
+ // prepare the existing type
+ var typeAnnotation = node.thisOrAncestorOfType<TypeAnnotation>();
+ if (typeAnnotation == null) {
+ return;
+ }
+ var type = typeAnnotation.type;
+ if (type == null || type.isDynamic || type.isDartAsyncStream) {
+ return;
+ }
+ _typeArgument = utils.getNodeText(typeAnnotation);
+
+ await builder.addDartFileEdit(file, (builder) {
+ builder.addReplacement(range.node(typeAnnotation), (builder) {
+ builder.writeType(typeProvider.streamType(type));
+ });
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
+ static ReplaceReturnTypeStream newInstance() => ReplaceReturnTypeStream();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index 59471b3..666ea13 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -1261,7 +1261,17 @@
static const REPLACE_RETURN_TYPE_FUTURE = FixKind(
'dart.fix.replace.returnTypeFuture',
DartFixKindPriority.DEFAULT,
- "Return 'Future' from 'async' function",
+ "Return 'Future<{0}>'",
+ );
+ static const REPLACE_RETURN_TYPE_ITERABLE = FixKind(
+ 'dart.fix.replace.returnTypeIterable',
+ DartFixKindPriority.DEFAULT,
+ "Return 'Iterable<{0}>'",
+ );
+ static const REPLACE_RETURN_TYPE_STREAM = FixKind(
+ 'dart.fix.replace.returnTypeStream',
+ DartFixKindPriority.DEFAULT,
+ "Return 'Stream<{0}>'",
);
static const REPLACE_CONTAINER_WITH_SIZED_BOX = FixKind(
'dart.fix.replace.containerWithSizedBox',
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 128e4eb..7abed7a 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -151,6 +151,8 @@
import 'package:analysis_server/src/services/correction/dart/replace_null_with_closure.dart';
import 'package:analysis_server/src/services/correction/dart/replace_return_type.dart';
import 'package:analysis_server/src/services/correction/dart/replace_return_type_future.dart';
+import 'package:analysis_server/src/services/correction/dart/replace_return_type_iterable.dart';
+import 'package:analysis_server/src/services/correction/dart/replace_return_type_stream.dart';
import 'package:analysis_server/src/services/correction/dart/replace_var_with_dynamic.dart';
import 'package:analysis_server/src/services/correction/dart/replace_with_brackets.dart';
import 'package:analysis_server/src/services/correction/dart/replace_with_conditional_assignment.dart';
@@ -842,9 +844,15 @@
CompileTimeErrorCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_3_PLUS: [
AddFieldFormalParameters.newInstance,
],
+ CompileTimeErrorCode.ILLEGAL_ASYNC_GENERATOR_RETURN_TYPE: [
+ ReplaceReturnTypeStream.newInstance,
+ ],
CompileTimeErrorCode.ILLEGAL_ASYNC_RETURN_TYPE: [
ReplaceReturnTypeFuture.newInstance,
],
+ CompileTimeErrorCode.ILLEGAL_SYNC_GENERATOR_RETURN_TYPE: [
+ ReplaceReturnTypeIterable.newInstance,
+ ],
CompileTimeErrorCode.IMPLEMENTS_NON_CLASS: [
ChangeTo.classOrMixin,
CreateClass.newInstance,
diff --git a/pkg/analysis_server/test/src/services/correction/fix/replace_return_type_future_test.dart b/pkg/analysis_server/test/src/services/correction/fix/replace_return_type_future_test.dart
index 9cfafa7..818dad5 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/replace_return_type_future_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/replace_return_type_future_test.dart
@@ -20,38 +20,14 @@
@override
FixKind get kind => DartFixKind.REPLACE_RETURN_TYPE_FUTURE;
- Future<void> test_adjacentNodes_withImport() async {
- await resolveTestCode('''
-import 'dart:async';
-var v;int main() async => 0;
-''');
- await assertHasFix('''
-import 'dart:async';
-var v;Future<int> main() async => 0;
-''', errorFilter: (error) {
- return error.errorCode == CompileTimeErrorCode.ILLEGAL_ASYNC_RETURN_TYPE;
- });
- }
-
- Future<void> test_adjacentNodes_withoutImport() async {
- await resolveTestCode('''
-var v;int main() async => 0;
-''');
- await assertHasFix('''
-var v;Future<int> main() async => 0;
-''');
- }
-
Future<void> test_complexTypeName_withImport() async {
await resolveTestCode('''
import 'dart:async';
-List<int> main() async {
-}
+List<int> f() async {}
''');
await assertHasFix('''
import 'dart:async';
-Future<List<int>> main() async {
-}
+Future<List<int>> f() async {}
''', errorFilter: (error) {
return error.errorCode == CompileTimeErrorCode.ILLEGAL_ASYNC_RETURN_TYPE;
});
@@ -59,25 +35,21 @@
Future<void> test_complexTypeName_withoutImport() async {
await resolveTestCode('''
-List<int> main() async {
-}
+List<int> f() async {}
''');
await assertHasFix('''
-Future<List<int>> main() async {
-}
+Future<List<int>> f() async {}
''');
}
Future<void> test_importedWithPrefix() async {
await resolveTestCode('''
import 'dart:async' as al;
-int main() async {
-}
+int f() async {}
''');
await assertHasFix('''
import 'dart:async' as al;
-al.Future<int> main() async {
-}
+al.Future<int> f() async {}
''', errorFilter: (error) {
return error.errorCode == CompileTimeErrorCode.ILLEGAL_ASYNC_RETURN_TYPE;
});
@@ -86,11 +58,11 @@
Future<void> test_simpleTypeName_withImport() async {
await resolveTestCode('''
import 'dart:async';
-int main() async => 0;
+int f() async {}
''');
await assertHasFix('''
import 'dart:async';
-Future<int> main() async => 0;
+Future<int> f() async {}
''', errorFilter: (error) {
return error.errorCode == CompileTimeErrorCode.ILLEGAL_ASYNC_RETURN_TYPE;
});
@@ -98,40 +70,10 @@
Future<void> test_simpleTypeName_withoutImport() async {
await resolveTestCode('''
-int main() async => 0;
+int f() async {}
''');
await assertHasFix('''
-Future<int> main() async => 0;
-''');
- }
-
- Future<void> test_withLibraryDirective_withImport() async {
- await resolveTestCode('''
-library main;
-import 'dart:async';
-int main() async {
-}
-''');
- await assertHasFix('''
-library main;
-import 'dart:async';
-Future<int> main() async {
-}
-''', errorFilter: (error) {
- return error.errorCode == CompileTimeErrorCode.ILLEGAL_ASYNC_RETURN_TYPE;
- });
- }
-
- Future<void> test_withLibraryDirective_withoutImport() async {
- await resolveTestCode('''
-library main;
-int main() async {
-}
-''');
- await assertHasFix('''
-library main;
-Future<int> main() async {
-}
+Future<int> f() async {}
''');
}
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/replace_return_type_iterable_test.dart b/pkg/analysis_server/test/src/services/correction/fix/replace_return_type_iterable_test.dart
new file mode 100644
index 0000000..e83a01c
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/replace_return_type_iterable_test.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2018, 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 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'fix_processor.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(ReplaceReturnTypeIterableTest);
+ });
+}
+
+@reflectiveTest
+class ReplaceReturnTypeIterableTest extends FixProcessorTest {
+ @override
+ FixKind get kind => DartFixKind.REPLACE_RETURN_TYPE_ITERABLE;
+
+ Future<void> test_complexTypeName() async {
+ await resolveTestCode('''
+List<int> f() sync* {}
+''');
+ await assertHasFix('''
+Iterable<List<int>> f() sync* {}
+''');
+ }
+
+ Future<void> test_importedWithPrefix() async {
+ await resolveTestCode('''
+import 'dart:core' as c;
+c.int f() sync* {}
+''');
+ await assertHasFix('''
+import 'dart:core' as c;
+c.Iterable<c.int> f() sync* {}
+''');
+ }
+
+ Future<void> test_simpleTypeName() async {
+ await resolveTestCode('''
+int f() sync* {}
+''');
+ await assertHasFix('''
+Iterable<int> f() sync* {}
+''');
+ }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/replace_return_type_stream_test.dart b/pkg/analysis_server/test/src/services/correction/fix/replace_return_type_stream_test.dart
new file mode 100644
index 0000000..e564385
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/replace_return_type_stream_test.dart
@@ -0,0 +1,82 @@
+// Copyright (c) 2018, 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 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'fix_processor.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(ReplaceReturnTypeStreamTest);
+ });
+}
+
+@reflectiveTest
+class ReplaceReturnTypeStreamTest extends FixProcessorTest {
+ @override
+ FixKind get kind => DartFixKind.REPLACE_RETURN_TYPE_STREAM;
+
+ Future<void> test_complexTypeName_withImport() async {
+ await resolveTestCode('''
+import 'dart:async';
+List<int> f() async* {}
+''');
+ await assertHasFix('''
+import 'dart:async';
+Stream<List<int>> f() async* {}
+''', errorFilter: (error) {
+ return error.errorCode ==
+ CompileTimeErrorCode.ILLEGAL_ASYNC_GENERATOR_RETURN_TYPE;
+ });
+ }
+
+ Future<void> test_complexTypeName_withoutImport() async {
+ await resolveTestCode('''
+List<int> f() async* {}
+''');
+ await assertHasFix('''
+Stream<List<int>> f() async* {}
+''');
+ }
+
+ Future<void> test_importedWithPrefix() async {
+ await resolveTestCode('''
+import 'dart:async' as al;
+int f() async* {}
+''');
+ await assertHasFix('''
+import 'dart:async' as al;
+al.Stream<int> f() async* {}
+''', errorFilter: (error) {
+ return error.errorCode ==
+ CompileTimeErrorCode.ILLEGAL_ASYNC_GENERATOR_RETURN_TYPE;
+ });
+ }
+
+ Future<void> test_simpleTypeName_withImport() async {
+ await resolveTestCode('''
+import 'dart:async';
+int f() async* {}
+''');
+ await assertHasFix('''
+import 'dart:async';
+Stream<int> f() async* {}
+''', errorFilter: (error) {
+ return error.errorCode ==
+ CompileTimeErrorCode.ILLEGAL_ASYNC_GENERATOR_RETURN_TYPE;
+ });
+ }
+
+ Future<void> test_simpleTypeName_withoutImport() async {
+ await resolveTestCode('''
+int f() async* {}
+''');
+ await assertHasFix('''
+Stream<int> f() async* {}
+''');
+ }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
index 46e09ff..3174635 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
@@ -184,6 +184,8 @@
import 'replace_new_with_const_test.dart' as replace_new_with_const;
import 'replace_null_with_closure_test.dart' as replace_null_with_closure;
import 'replace_return_type_future_test.dart' as replace_return_type_future;
+import 'replace_return_type_iterable_test.dart' as replace_return_type_iterable;
+import 'replace_return_type_stream_test.dart' as replace_return_type_stream;
import 'replace_return_type_test.dart' as replace_return_type;
import 'replace_var_with_dynamic_test.dart' as replace_var_with_dynamic;
import 'replace_with_brackets_test.dart' as replace_with_brackets;
@@ -372,6 +374,8 @@
replace_null_with_void.main();
replace_return_type.main();
replace_return_type_future.main();
+ replace_return_type_iterable.main();
+ replace_return_type_stream.main();
replace_var_with_dynamic.main();
replace_with_brackets.main();
replace_with_conditional_assignment.main();
diff --git a/pkg/analyzer/lib/dart/element/type.dart b/pkg/analyzer/lib/dart/element/type.dart
index 923cc3f..e3f102e 100644
--- a/pkg/analyzer/lib/dart/element/type.dart
+++ b/pkg/analyzer/lib/dart/element/type.dart
@@ -57,6 +57,10 @@
/// the dart:async library.
bool get isDartAsyncFutureOr;
+ /// Return `true` if this type represents the type 'Stream' defined in the
+ /// dart:async library.
+ bool get isDartAsyncStream;
+
/// Return `true` if this type represents the type 'bool' defined in the
/// dart:core library.
bool get isDartCoreBool;
diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart
index bbfa073..70f398c 100644
--- a/pkg/analyzer/lib/src/dart/element/type.dart
+++ b/pkg/analyzer/lib/src/dart/element/type.dart
@@ -517,6 +517,11 @@
}
@override
+ bool get isDartAsyncStream {
+ return element.name == "Stream" && element.library.isDartAsync;
+ }
+
+ @override
bool get isDartCoreBool {
return element.name == "bool" && element.library.isDartCore;
}
@@ -974,6 +979,9 @@
bool get isDartAsyncFutureOr => false;
@override
+ bool get isDartAsyncStream => false;
+
+ @override
bool get isDartCoreBool => false;
@override
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
index 104fc79..23110a9 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
@@ -1486,9 +1486,9 @@
return;
}
- addReplacement(range.node(typeAnnotation!), (EditBuilder builder) {
+ addReplacement(range.node(typeAnnotation!), (builder) {
var futureType = typeProvider.futureType(type);
- if (!(builder as DartEditBuilder).writeType(futureType)) {
+ if (!builder.writeType(futureType)) {
builder.write('void');
}
});