Migration: fix handling of unresolved field formal parameters.

Change-Id: I67d3472974657618afa88d7feafae1eaf8250e43
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/207040
Reviewed-by: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
Paul Berry 2021-07-15 16:22:09 +00:00
parent 06a35b8c2e
commit e98091010e
5 changed files with 89 additions and 27 deletions

View file

@ -895,18 +895,20 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
_dispatch(node.parameters);
var parameterElement = node.declaredElement as FieldFormalParameterElement;
var parameterType = _variables!.decoratedElementType(parameterElement);
var field = parameterElement.field!;
_fieldsNotInitializedByConstructor!.remove(field);
var fieldType = _variables!.decoratedElementType(field);
var origin = FieldFormalParameterOrigin(source, node);
if (node.type == null) {
_linkDecoratedTypes(parameterType, fieldType, origin, isUnion: false);
_checkAssignment(origin, FixReasonTarget.root,
source: fieldType, destination: parameterType, hard: false);
} else {
_dispatch(node.type);
_checkAssignment(origin, FixReasonTarget.root,
source: parameterType, destination: fieldType, hard: true);
var field = parameterElement.field;
if (field != null) {
_fieldsNotInitializedByConstructor!.remove(field);
var fieldType = _variables!.decoratedElementType(field);
var origin = FieldFormalParameterOrigin(source, node);
if (node.type == null) {
_linkDecoratedTypes(parameterType, fieldType, origin, isUnion: false);
_checkAssignment(origin, FixReasonTarget.root,
source: fieldType, destination: parameterType, hard: false);
} else {
_dispatch(node.type);
_checkAssignment(origin, FixReasonTarget.root,
source: parameterType, destination: fieldType, hard: true);
}
}
return null;
@ -2083,7 +2085,10 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
var result = <PropertyAccessorElement, FieldFormalParameterElement>{};
for (var parameter in constructorElement.parameters) {
if (parameter is FieldFormalParameterElement) {
result[parameter.field!.getter!] = parameter;
var getter = parameter.field?.getter;
if (getter != null) {
result[getter] = parameter;
}
}
}
return result;

View file

@ -1147,15 +1147,18 @@ class _FixBuilderPreVisitor extends GeneralizingAstVisitor<void>
// Potentially add an explicit type to a field formal parameter.
var decl = node.declaredElement as FieldFormalParameterElement;
var decoratedType = _fixBuilder._variables!.decoratedElementType(decl);
var decoratedFieldType =
_fixBuilder._variables!.decoratedElementType(decl.field!);
var typeToAdd = _fixBuilder._variables!.toFinalType(decoratedType);
var fieldFinalType =
_fixBuilder._variables!.toFinalType(decoratedFieldType);
if (typeToAdd is InterfaceType &&
!_fixBuilder._typeSystem.isSubtypeOf(fieldFinalType, typeToAdd)) {
(_fixBuilder._getChange(node) as NodeChangeForFieldFormalParameter)
.addExplicitType = typeToAdd;
var field = decl.field;
if (field != null) {
var decoratedFieldType =
_fixBuilder._variables!.decoratedElementType(field);
var typeToAdd = _fixBuilder._variables!.toFinalType(decoratedType);
var fieldFinalType =
_fixBuilder._variables!.toFinalType(decoratedFieldType);
if (typeToAdd is InterfaceType &&
!_fixBuilder._typeSystem.isSubtypeOf(fieldFinalType, typeToAdd)) {
(_fixBuilder._getChange(node) as NodeChangeForFieldFormalParameter)
.addExplicitType = typeToAdd;
}
}
} else if (node.parameters != null) {
// Handle function-typed field formal parameters.

View file

@ -446,9 +446,10 @@ class NodeBuilder extends GeneralizingAstVisitor<DecoratedType>
@override
DecoratedType? visitMethodDeclaration(MethodDeclaration node) {
_handleExecutableDeclaration(
var declaredElement = node.declaredElement;
var decoratedType = _handleExecutableDeclaration(
node,
node.declaredElement!,
declaredElement!,
node.metadata,
node.returnType,
node.typeParameters,
@ -456,6 +457,19 @@ class NodeBuilder extends GeneralizingAstVisitor<DecoratedType>
null,
node.body,
null);
if (declaredElement is PropertyAccessorElement) {
// Store a decorated type for the synthetic field so that in case we try
// to access it later we won't crash (this could happen due to errors in
// the source code).
if (declaredElement.isGetter) {
_variables!.recordDecoratedElementType(
declaredElement.variable, decoratedType.returnType);
} else {
_variables!.recordDecoratedElementType(
declaredElement.variable, decoratedType.positionalParameters![0],
soft: true);
}
}
return null;
}
@ -675,7 +689,7 @@ class NodeBuilder extends GeneralizingAstVisitor<DecoratedType>
}
/// Common handling of function and method declarations.
void _handleExecutableDeclaration(
DecoratedType _handleExecutableDeclaration(
AstNode node,
ExecutableElement declaredElement,
NodeList<Annotation>? metadata,
@ -726,6 +740,7 @@ class NodeBuilder extends GeneralizingAstVisitor<DecoratedType>
}
_variables!
.recordDecoratedElementType(declaredElement, decoratedFunctionType);
return decoratedFunctionType;
}
DecoratedType? _handleFormalParameter(

View file

@ -214,7 +214,8 @@ class Variables {
}
/// Associates decorated type information with the given [element].
void recordDecoratedElementType(Element? element, DecoratedType? type) {
void recordDecoratedElementType(Element? element, DecoratedType? type,
{bool soft = false}) {
assert(() {
assert(element is! TypeParameterElement,
'Use recordDecoratedTypeParameterBound instead');
@ -227,6 +228,9 @@ class Variables {
}
return true;
}());
if (soft && _decoratedElementTypes.containsKey(element)) {
return;
}
_decoratedElementTypes[element] = type;
}

View file

@ -116,7 +116,7 @@ abstract class _ProvisionalApiTestBase extends AbstractContextTest {
///
/// Optional parameter [removeViaComments] indicates whether dead code should
/// be removed in its entirety (the default) or removed by commenting it out.
Future<void> _checkSingleFileChanges(String content, String expected,
Future<void> _checkSingleFileChanges(String content, dynamic expected,
{Map<String, String> migratedInput = const {},
bool removeViaComments = false,
bool warnOnWeakCode = false,
@ -1164,6 +1164,41 @@ void main() {
await _checkSingleFileChanges(content, expected);
}
Future<void> test_constructor_field_formal_resolves_to_getter() async {
var content = '''
class C {
int get i => 0;
C(this.i);
}
''';
// It doesn't matter what the migration produces; we just want to make sure
// there isn't a crash.
await _checkSingleFileChanges(content, anything, allowErrors: true);
}
Future<void> test_constructor_field_formal_resolves_to_setter() async {
var content = '''
class C {
set i(int value) {}
C(this.i);
}
''';
// It doesn't matter what the migration produces; we just want to make sure
// there isn't a crash.
await _checkSingleFileChanges(content, anything, allowErrors: true);
}
Future<void> test_constructor_field_formal_unresolved() async {
var content = '''
class C {
C(this.i);
}
''';
// It doesn't matter what the migration produces; we just want to make sure
// there isn't a crash.
await _checkSingleFileChanges(content, anything, allowErrors: true);
}
Future<void> test_constructor_optional_param_factory() async {
var content = '''
class C {