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>
This commit is contained in:
Brian Wilkerson
2021-12-23 17:36:42 +00:00
parent bd555f2760
commit eaef692c3f
12 changed files with 274 additions and 71 deletions
@@ -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<Object>? 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);
@@ -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();
}
@@ -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();
}
@@ -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',
@@ -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,
@@ -20,38 +20,14 @@ class ReplaceReturnTypeFutureTest extends FixProcessorTest {
@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<List<int>> main() async {
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 @@ al.Future<int> main() async {
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<int> main() async => 0;
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 {}
''');
}
}
@@ -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* {}
''');
}
}
@@ -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* {}
''');
}
}
@@ -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();
+4
View File
@@ -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;
@@ -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;
@@ -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');
}
});