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 9d3b8ddf5f9..dfa48e40bf1 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 @@ -12,16 +12,25 @@ class ReplaceReturnTypeFuture extends CorrectionProducer { /// The text for the type argument to 'Future'. String _typeArgument = ''; + @override + bool get canBeAppliedInBulk => true; + + @override + bool get canBeAppliedToFile => true; + @override List? get fixArguments => [_typeArgument]; @override FixKind get fixKind => DartFixKind.REPLACE_RETURN_TYPE_FUTURE; + @override + FixKind get multiFixKind => DartFixKind.REPLACE_RETURN_TYPE_FUTURE_MULTI; + @override Future compute(ChangeBuilder builder) async { // prepare the existing type - var typeAnnotation = node.thisOrAncestorOfType(); + var typeAnnotation = _getTypeAnnotation(node); if (typeAnnotation == null) { return; } @@ -34,4 +43,13 @@ class ReplaceReturnTypeFuture extends CorrectionProducer { /// Return an instance of this class. Used as a tear-off in `FixProcessor`. static ReplaceReturnTypeFuture newInstance() => ReplaceReturnTypeFuture(); + + static TypeAnnotation? _getTypeAnnotation(AstNode node) { + var function = node.thisOrAncestorOfType(); + if (function != null) { + return function.returnType; + } + var method = node.thisOrAncestorOfType(); + return method?.returnType; + } } diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart index 51f8992d021..5a003bb126f 100644 --- a/pkg/analysis_server/lib/src/services/correction/fix.dart +++ b/pkg/analysis_server/lib/src/services/correction/fix.dart @@ -1288,6 +1288,11 @@ class DartFixKind { DartFixKindPriority.DEFAULT, "Return 'Future<{0}>'", ); + static const REPLACE_RETURN_TYPE_FUTURE_MULTI = FixKind( + 'dart.fix.replace.returnTypeFuture.multi', + DartFixKindPriority.IN_FILE, + "Return a 'Future' where required in file.", + ); static const REPLACE_RETURN_TYPE_ITERABLE = FixKind( 'dart.fix.replace.returnTypeIterable', DartFixKindPriority.DEFAULT, 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 c3cdf0af78d..63b3585ee7b 100644 --- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart +++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart @@ -407,6 +407,9 @@ class FixProcessor extends BaseProcessor { LintNames.avoid_unnecessary_containers: [ FlutterRemoveWidget.newInstance, ], + LintNames.avoid_void_async: [ + ReplaceReturnTypeFuture.newInstance, + ], LintNames.await_only_futures: [ RemoveAwait.newInstance, ], diff --git a/pkg/analysis_server/lib/src/services/linter/lint_names.dart b/pkg/analysis_server/lib/src/services/linter/lint_names.dart index c5df9b7b14f..1a6d4e273a1 100644 --- a/pkg/analysis_server/lib/src/services/linter/lint_names.dart +++ b/pkg/analysis_server/lib/src/services/linter/lint_names.dart @@ -42,6 +42,7 @@ class LintNames { 'avoid_unused_constructor_parameters'; static const String avoid_unnecessary_containers = 'avoid_unnecessary_containers'; + static const String avoid_void_async = 'avoid_void_async'; static const String await_only_futures = 'await_only_futures'; static const String curly_braces_in_flow_control_structures = 'curly_braces_in_flow_control_structures'; 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 818dad551c7..9f22bf1352f 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 @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'package:analysis_server/src/services/correction/fix.dart'; +import 'package:analysis_server/src/services/linter/lint_names.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'; @@ -11,10 +12,117 @@ import 'fix_processor.dart'; void main() { defineReflectiveSuite(() { + defineReflectiveTests(ReplaceReturnTypeFutureLintBulkTest); + defineReflectiveTests(ReplaceReturnTypeFutureLintTest); defineReflectiveTests(ReplaceReturnTypeFutureTest); }); } +@reflectiveTest +class ReplaceReturnTypeFutureLintBulkTest extends BulkFixProcessorTest { + @override + String get lintCode => LintNames.avoid_void_async; + + Future test_bulk() async { + await resolveTestCode(''' +void f1() async {} + +void f2() async => null; + +class C { + void m1() async {} + + void m2() async => null; + + void m3() async { + void f() async {}; + f(); + } +} +'''); + await assertHasFix(''' +Future f1() async {} + +Future f2() async => null; + +class C { + Future m1() async {} + + Future m2() async => null; + + Future m3() async { + Future f() async {}; + f(); + } +} +'''); + } +} + +@reflectiveTest +class ReplaceReturnTypeFutureLintTest extends FixProcessorLintTest { + @override + FixKind get kind => DartFixKind.REPLACE_RETURN_TYPE_FUTURE; + + @override + String get lintCode => LintNames.avoid_void_async; + + Future test_function() async { + await resolveTestCode('void f() async {}'); + await assertHasFix('Future f() async {}'); + } + + Future test_functionInMethod() async { + await resolveTestCode(''' +class C { + void m() { + void f() async {}; + f(); + } +} +'''); + await assertHasFix(''' +class C { + void m() { + Future f() async {}; + f(); + } +} +'''); + } + + Future test_functionReturnNull() async { + await resolveTestCode('void f() async => null;'); + await assertHasFix('Future f() async => null;'); + } + + Future test_method() async { + await resolveTestCode(''' +class C { + void m() async {} +} +'''); + await assertHasFix(''' +class C { + Future m() async {} +} +'''); + } + + Future test_methodReturnNull() async { + await resolveTestCode(''' +class C { + void m() async => null; +} +'''); + await assertHasFix(''' +class C { + Future m() async => null; +} +'''); + } +} + @reflectiveTest class ReplaceReturnTypeFutureTest extends FixProcessorTest { @override @@ -55,6 +163,19 @@ al.Future f() async {} }); } + Future test_method() async { + await resolveTestCode(''' +class C { + int m() async {} +} +'''); + await assertHasFix(''' +class C { + Future m() async {} +} +'''); + } + Future test_simpleTypeName_withImport() async { await resolveTestCode(''' import 'dart:async';