DartEditBuilder to have canWriteType()

Bug: 46911
Change-Id: Icf8cdb3af6f2c34fe2eff75f1aeb1dac4aa8aac3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210463
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Ahmed Ashour 2021-08-21 18:51:36 +00:00 committed by commit-bot@chromium.org
parent 86c660faaa
commit 86898c2ee6
5 changed files with 111 additions and 42 deletions

View file

@ -12,7 +12,6 @@ import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/element/type_system.dart';
import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/assist/assist.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
@ -63,27 +62,23 @@ class AddTypeAnnotation extends CorrectionProducer {
Future<void> _applyChange(
ChangeBuilder builder, Token? keyword, int offset, DartType type) async {
Future<bool> tryToApplyChange(ChangeBuilder builder) async {
var validChange = true;
await builder.addDartFileEdit(file, (builder) {
_configureTargetLocation(node);
await builder.addDartFileEdit(file, (builder) {
if (builder.canWriteType(type)) {
if (keyword != null && keyword.keyword == Keyword.VAR) {
builder.addReplacement(range.token(keyword), (builder) {
validChange = builder.writeType(type);
builder.writeType(type);
});
} else {
builder.addInsertion(offset, (builder) {
validChange = builder.writeType(type);
builder.writeType(type);
builder.write(' ');
});
}
});
return validChange;
}
_configureTargetLocation(node);
if (await tryToApplyChange(_temporaryBuilder(builder))) {
await tryToApplyChange(builder);
}
}
});
}
/// Configure the [utils] using the given [target].
@ -158,9 +153,6 @@ class AddTypeAnnotation extends CorrectionProducer {
await _applyChange(builder, declarationList.keyword, variable.offset, type);
}
ChangeBuilder _temporaryBuilder(ChangeBuilder builder) =>
ChangeBuilder(workspace: (builder as ChangeBuilderImpl).workspace);
DartType? _typeForVariable(VariableDeclaration variable) {
var initializer = variable.initializer;
if (initializer != null) {

View file

@ -6,7 +6,6 @@ import 'package:_fe_analyzer_shared/src/scanner/token.dart';
import 'package:analysis_server/src/services/correction/assist.dart';
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/assist/assist.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/range_factory.dart';
@ -51,34 +50,26 @@ class SplitVariableDeclaration extends CorrectionProducer {
return;
}
Future<bool> tryToApplyChange(ChangeBuilder builder) async {
var validChange = true;
await builder.addDartFileEdit(file, (builder) {
if (variableList.type == null) {
final type = variable.declaredElement!.type;
if (!type.isDynamic && keyword != null) {
builder.addReplacement(range.token(keyword), (builder) {
validChange = builder.writeType(type);
});
await builder.addDartFileEdit(file, (builder) {
if (variableList.type == null) {
final type = variable.declaredElement!.type;
if (!type.isDynamic && keyword != null) {
if (!builder.canWriteType(type)) {
return;
}
builder.addReplacement(range.token(keyword), (builder) {
builder.writeType(type);
});
}
}
var indent = utils.getNodePrefix(statement);
var name = variable.name.name;
builder.addSimpleInsertion(
variable.name.end, ';' + eol + indent + name);
});
return validChange;
}
if (await tryToApplyChange(_temporaryBuilder(builder))) {
await tryToApplyChange(builder);
}
var indent = utils.getNodePrefix(statement);
var name = variable.name.name;
builder.addSimpleInsertion(
variable.name.end, ';' + eol + indent + name);
});
}
ChangeBuilder _temporaryBuilder(ChangeBuilder builder) =>
ChangeBuilder(workspace: (builder as ChangeBuilderImpl).workspace);
/// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
static SplitVariableDeclaration newInstance() => SplitVariableDeclaration();
}

View file

@ -119,7 +119,7 @@ main() {
''');
}
Future<void> test_unknownType() async {
Future<void> test_privateType() async {
addSource('/home/test/lib/a.dart', '''
class A {
_B b => _B();

View file

@ -56,6 +56,12 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder {
super.addLinkedEdit(groupName,
(builder) => buildLinkedEdit(builder as DartLinkedEditBuilder));
@override
bool canWriteType(DartType? type, {ExecutableElement? methodBeingCopied}) =>
type != null && !type.isDynamic
? _canWriteType(type, methodBeingCopied: methodBeingCopied)
: false;
@override
LinkedEditBuilderImpl createLinkedEditBuilder() {
return DartLinkedEditBuilderImpl(this);
@ -838,6 +844,60 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder {
}
}
/// Check if the code to reference [type] in this compilation unit can be
/// written.
///
/// See also [_writeType]
bool _canWriteType(DartType? type,
{ExecutableElement? methodBeingCopied, bool required = false}) {
type = _getVisibleType(type, methodBeingCopied: methodBeingCopied);
// If not a useful type, don't write it.
if (type == null) {
return false;
}
if (type.isDynamic) {
if (required) {
return true;
}
return false;
}
if (type.isBottom) {
var library = dartFileEditBuilder.resolvedUnit.libraryElement;
if (library.isNonNullableByDefault) {
return true;
}
return false;
}
var alias = type.alias;
if (alias != null) {
return true;
}
if (type is FunctionType) {
return true;
}
if (type is InterfaceType) {
return true;
}
if (type is NeverType) {
return true;
}
if (type is TypeParameterType) {
return true;
}
if (type is VoidType) {
return true;
}
throw UnimplementedError('(${type.runtimeType}) $type');
}
/// Generate a name that does not occur in [existingNames] that begins with
/// the given [prefix].
String _generateUniqueName(Set<String> existingNames, String prefix) {
@ -1300,6 +1360,12 @@ class DartFileEditBuilderImpl extends FileEditBuilderImpl
super.addReplacement(
range, (builder) => buildEdit(builder as DartEditBuilder));
@override
bool canWriteType(DartType? type, {ExecutableElement? methodBeingCopied}) {
var builder = createEditBuilder(0, 0);
return builder.canWriteType(type, methodBeingCopied: methodBeingCopied);
}
@override
void convertFunctionFromSyncToAsync(
FunctionBody? body, TypeProvider typeProvider) {

View file

@ -20,6 +20,17 @@ abstract class DartEditBuilder implements EditBuilder {
void addLinkedEdit(String groupName,
void Function(DartLinkedEditBuilder builder) buildLinkedEdit);
/// Check if the code for a type annotation for the given [type] can be
/// written.
///
/// If a [methodBeingCopied] is provided, then type parameters defined by that
/// method are assumed to be part of what is being written and hence valid
/// types.
///
/// The logic is the same as the one used in [writeType]
bool canWriteType(DartType? type,
{ExecutableElement? methodBeingCopied});
/// Write the code for a declaration of a class with the given [name]. If a
/// list of [interfaces] is provided, then the class will implement those
/// interfaces. If [isAbstract] is `true`, then the class will be abstract. If
@ -299,6 +310,15 @@ abstract class DartFileEditBuilder implements FileEditBuilder {
void addReplacement(
SourceRange range, void Function(DartEditBuilder builder) buildEdit);
/// Check if the code for a type annotation for the given [type] can be
/// written.
///
/// If a [methodBeingCopied] is provided, then type parameters defined by that
/// method are assumed to be part of what is being written and hence valid
/// types.
bool canWriteType(DartType? type,
{ExecutableElement? methodBeingCopied});
/// Create one or more edits that will convert the given function [body] from
/// being synchronous to be asynchronous. This includes adding the `async`
/// modifier to the body as well as potentially replacing the return type of