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 fb29e4fda27..9d3b8ddf5f9 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/change_builder/change_builder_core.dar import 'package:analyzer_plugin/utilities/fixes/fixes.dart'; class ReplaceReturnTypeFuture extends CorrectionProducer { + /// The text for the type argument to 'Future'. + String _typeArgument = ''; + + @override + List? get fixArguments => [_typeArgument]; + @override FixKind get fixKind => DartFixKind.REPLACE_RETURN_TYPE_FUTURE; @@ -19,6 +25,7 @@ class ReplaceReturnTypeFuture extends CorrectionProducer { 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 00000000000..cc491aae1d6 --- /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? get fixArguments => [_typeArgument]; + + @override + FixKind get fixKind => DartFixKind.REPLACE_RETURN_TYPE_ITERABLE; + + @override + Future compute(ChangeBuilder builder) async { + // prepare the existing type + var typeAnnotation = node.thisOrAncestorOfType(); + 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 00000000000..829ebe66a74 --- /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? get fixArguments => [_typeArgument]; + + @override + FixKind get fixKind => DartFixKind.REPLACE_RETURN_TYPE_STREAM; + + @override + Future compute(ChangeBuilder builder) async { + // prepare the existing type + var typeAnnotation = node.thisOrAncestorOfType(); + 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 59471b37445..666ea1321e3 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 @@ class DartFixKind { 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 128e4eb3eb0..7abed7aa718 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_new_with_co 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 @@ class FixProcessor extends BaseProcessor { 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 9cfafa75c68..818dad551c7 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 @@ class ReplaceReturnTypeFutureTest extends FixProcessorTest { @override FixKind get kind => DartFixKind.REPLACE_RETURN_TYPE_FUTURE; - Future test_adjacentNodes_withImport() async { - await resolveTestCode(''' -import 'dart:async'; -var v;int main() async => 0; -'''); - await assertHasFix(''' -import 'dart:async'; -var v;Future main() async => 0; -''', errorFilter: (error) { - return error.errorCode == CompileTimeErrorCode.ILLEGAL_ASYNC_RETURN_TYPE; - }); - } - - Future test_adjacentNodes_withoutImport() async { - await resolveTestCode(''' -var v;int main() async => 0; -'''); - await assertHasFix(''' -var v;Future main() async => 0; -'''); - } - Future test_complexTypeName_withImport() async { await resolveTestCode(''' import 'dart:async'; -List main() async { -} +List f() async {} '''); await assertHasFix(''' import 'dart:async'; -Future> main() async { -} +Future> f() async {} ''', errorFilter: (error) { return error.errorCode == CompileTimeErrorCode.ILLEGAL_ASYNC_RETURN_TYPE; }); @@ -59,25 +35,21 @@ Future> main() async { Future test_complexTypeName_withoutImport() async { await resolveTestCode(''' -List main() async { -} +List f() async {} '''); await assertHasFix(''' -Future> main() async { -} +Future> f() async {} '''); } Future 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 main() async { -} +al.Future f() async {} ''', errorFilter: (error) { return error.errorCode == CompileTimeErrorCode.ILLEGAL_ASYNC_RETURN_TYPE; }); @@ -86,11 +58,11 @@ al.Future main() async { Future test_simpleTypeName_withImport() async { await resolveTestCode(''' import 'dart:async'; -int main() async => 0; +int f() async {} '''); await assertHasFix(''' import 'dart:async'; -Future main() async => 0; +Future f() async {} ''', errorFilter: (error) { return error.errorCode == CompileTimeErrorCode.ILLEGAL_ASYNC_RETURN_TYPE; }); @@ -98,40 +70,10 @@ Future main() async => 0; Future test_simpleTypeName_withoutImport() async { await resolveTestCode(''' -int main() async => 0; +int f() async {} '''); await assertHasFix(''' -Future main() async => 0; -'''); - } - - Future test_withLibraryDirective_withImport() async { - await resolveTestCode(''' -library main; -import 'dart:async'; -int main() async { -} -'''); - await assertHasFix(''' -library main; -import 'dart:async'; -Future main() async { -} -''', errorFilter: (error) { - return error.errorCode == CompileTimeErrorCode.ILLEGAL_ASYNC_RETURN_TYPE; - }); - } - - Future test_withLibraryDirective_withoutImport() async { - await resolveTestCode(''' -library main; -int main() async { -} -'''); - await assertHasFix(''' -library main; -Future main() async { -} +Future 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 00000000000..e83a01c536e --- /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 test_complexTypeName() async { + await resolveTestCode(''' +List f() sync* {} +'''); + await assertHasFix(''' +Iterable> f() sync* {} +'''); + } + + Future test_importedWithPrefix() async { + await resolveTestCode(''' +import 'dart:core' as c; +c.int f() sync* {} +'''); + await assertHasFix(''' +import 'dart:core' as c; +c.Iterable f() sync* {} +'''); + } + + Future test_simpleTypeName() async { + await resolveTestCode(''' +int f() sync* {} +'''); + await assertHasFix(''' +Iterable 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 00000000000..e564385d4e0 --- /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 test_complexTypeName_withImport() async { + await resolveTestCode(''' +import 'dart:async'; +List f() async* {} +'''); + await assertHasFix(''' +import 'dart:async'; +Stream> f() async* {} +''', errorFilter: (error) { + return error.errorCode == + CompileTimeErrorCode.ILLEGAL_ASYNC_GENERATOR_RETURN_TYPE; + }); + } + + Future test_complexTypeName_withoutImport() async { + await resolveTestCode(''' +List f() async* {} +'''); + await assertHasFix(''' +Stream> f() async* {} +'''); + } + + Future test_importedWithPrefix() async { + await resolveTestCode(''' +import 'dart:async' as al; +int f() async* {} +'''); + await assertHasFix(''' +import 'dart:async' as al; +al.Stream f() async* {} +''', errorFilter: (error) { + return error.errorCode == + CompileTimeErrorCode.ILLEGAL_ASYNC_GENERATOR_RETURN_TYPE; + }); + } + + Future test_simpleTypeName_withImport() async { + await resolveTestCode(''' +import 'dart:async'; +int f() async* {} +'''); + await assertHasFix(''' +import 'dart:async'; +Stream f() async* {} +''', errorFilter: (error) { + return error.errorCode == + CompileTimeErrorCode.ILLEGAL_ASYNC_GENERATOR_RETURN_TYPE; + }); + } + + Future test_simpleTypeName_withoutImport() async { + await resolveTestCode(''' +int f() async* {} +'''); + await assertHasFix(''' +Stream 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 46e09ffbbdc..3174635a4fd 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_final_with_var_test.dart' as replace_final_with_var; 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 @@ void main() { 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 923cc3fcc61..e3f102e4745 100644 --- a/pkg/analyzer/lib/dart/element/type.dart +++ b/pkg/analyzer/lib/dart/element/type.dart @@ -57,6 +57,10 @@ abstract class DartType { /// 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 bbfa073fe4c..70f398c0b6f 100644 --- a/pkg/analyzer/lib/src/dart/element/type.dart +++ b/pkg/analyzer/lib/src/dart/element/type.dart @@ -516,6 +516,11 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType { return element.name == "FutureOr" && element.library.isDartAsync; } + @override + bool get isDartAsyncStream { + return element.name == "Stream" && element.library.isDartAsync; + } + @override bool get isDartCoreBool { return element.name == "bool" && element.library.isDartCore; @@ -973,6 +978,9 @@ abstract class TypeImpl implements DartType { @override bool get isDartAsyncFutureOr => false; + @override + bool get isDartAsyncStream => false; + @override bool get isDartCoreBool => false; 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 104fc798f46..23110a9dbe4 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 @@ class DartFileEditBuilderImpl extends FileEditBuilderImpl 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'); } });