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'); } });