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:
@@ -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);
|
||||
|
||||
+44
@@ -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,
|
||||
|
||||
+10
-68
@@ -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 {}
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
+50
@@ -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* {}
|
||||
''');
|
||||
}
|
||||
}
|
||||
+82
@@ -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();
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user