Implicit cast of RecordLiteral fields from dynamic, tests, also for ImplicitCallReference.

Change-Id: I3a04b404cf7743c3cc383a83fa58080a3b7b294f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/262662
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2022-10-04 19:40:28 +00:00 committed by Commit Queue
parent f042822993
commit 8e9ad1bd7a
3 changed files with 194 additions and 3 deletions

View file

@ -123,17 +123,26 @@ class RecordLiteralResolver {
);
}
void _resolveField(Expression field, DartType? contextType) {
void _resolveField(ExpressionImpl field, DartType? contextType) {
_resolver.analyzeExpression(field, contextType);
_resolver.popRewrite();
field = _resolver.popRewrite()!;
// Implicit cast from `dynamic`.
if (contextType != null && field.typeOrThrow.isDynamic) {
field.staticType = contextType;
if (field is NamedExpressionImpl) {
field.expression.staticType = contextType;
}
}
}
void _resolveFields(RecordLiteralImpl node, DartType? contextType) {
if (contextType is RecordType) {
var index = 0;
for (final field in node.fields) {
field as ExpressionImpl;
DartType? fieldContextType;
if (field is NamedExpression) {
if (field is NamedExpressionImpl) {
final name = field.name.label.name;
fieldContextType = contextType.namedField(name)?.type;
} else {
@ -146,6 +155,7 @@ class RecordLiteralResolver {
}
} else {
for (final field in node.fields) {
field as ExpressionImpl;
_resolveField(field, null);
}
}

View file

@ -14,6 +14,177 @@ main() {
@reflectiveTest
class RecordLiteralTest extends PubPackageResolutionTest {
test_field_rewrite_named() async {
await assertNoErrorsInCode(r'''
void f((int, String) r) {
(f1: r.$0, );
}
''');
final node = findNode.recordLiteral('(f1');
assertResolvedNodeText(node, r'''
RecordLiteral
leftParenthesis: (
fields
NamedExpression
name: Label
label: SimpleIdentifier
token: f1
staticElement: <null>
staticType: null
colon: :
expression: PropertyAccess
target: SimpleIdentifier
token: r
staticElement: self::@function::f::@parameter::r
staticType: (int, String)
operator: .
propertyName: SimpleIdentifier
token: $0
staticElement: <null>
staticType: int
staticType: int
rightParenthesis: )
staticType: ({int f1})
''');
}
test_field_rewrite_positional() async {
await assertNoErrorsInCode(r'''
void f((int, String) r) {
(r.$0, );
}
''');
final node = findNode.recordLiteral('(r');
assertResolvedNodeText(node, r'''
RecordLiteral
leftParenthesis: (
fields
PropertyAccess
target: SimpleIdentifier
token: r
staticElement: self::@function::f::@parameter::r
staticType: (int, String)
operator: .
propertyName: SimpleIdentifier
token: $0
staticElement: <null>
staticType: int
staticType: int
rightParenthesis: )
staticType: (int)
''');
}
test_hasContext_implicitCallReference_named() async {
await assertNoErrorsInCode(r'''
class A {
void call() {}
}
final a = A();
final ({void Function() f1}) x = (f1: a);
''');
final node = findNode.recordLiteral('(f1');
assertResolvedNodeText(node, r'''
RecordLiteral
leftParenthesis: (
fields
NamedExpression
name: Label
label: SimpleIdentifier
token: f1
staticElement: <null>
staticType: null
colon: :
expression: ImplicitCallReference
expression: SimpleIdentifier
token: a
staticElement: self::@getter::a
staticType: A
staticElement: self::@class::A::@method::call
staticType: void Function()
rightParenthesis: )
staticType: ({void Function() f1})
''');
}
test_hasContext_implicitCallReference_positional() async {
await assertNoErrorsInCode(r'''
class A {
void call() {}
}
final a = A();
final (void Function(), ) x = (a, );
''');
final node = findNode.recordLiteral('(a');
assertResolvedNodeText(node, r'''
RecordLiteral
leftParenthesis: (
fields
ImplicitCallReference
expression: SimpleIdentifier
token: a
staticElement: self::@getter::a
staticType: A
staticElement: self::@class::A::@method::call
staticType: void Function()
rightParenthesis: )
staticType: (void Function())
''');
}
test_hasContext_implicitCast_fromDynamic_named() async {
await assertNoErrorsInCode(r'''
final dynamic a = 0;
final ({int f1}) x = (f1: a);
''');
final node = findNode.recordLiteral('(f1');
assertResolvedNodeText(node, r'''
RecordLiteral
leftParenthesis: (
fields
NamedExpression
name: Label
label: SimpleIdentifier
token: f1
staticElement: <null>
staticType: null
colon: :
expression: SimpleIdentifier
token: a
staticElement: self::@getter::a
staticType: int
rightParenthesis: )
staticType: ({int f1})
''');
}
test_hasContext_implicitCast_fromDynamic_positional() async {
await assertNoErrorsInCode(r'''
final dynamic a = 0;
final (int, ) x = (a, );
''');
final node = findNode.recordLiteral('(a');
assertResolvedNodeText(node, r'''
RecordLiteral
leftParenthesis: (
fields
SimpleIdentifier
token: a
staticElement: self::@getter::a
staticType: int
rightParenthesis: )
staticType: (int)
''');
}
test_hasContext_mixed() async {
await assertNoErrorsInCode(r'''
class A1 {}

View file

@ -924,6 +924,16 @@ class ResolvedAstPrinter extends ThrowingAstVisitor<void> {
_withIndent(() {
_writeNamedChildEntities(node);
_writeParameterElement(node);
// Types of the node and its expression must be the same.
if (node.expression.staticType != node.staticType) {
final nodeType = node.staticType;
final expressionType = node.expression.staticType;
fail(
'Must be the same:\n'
'nodeType: $nodeType\n'
'expressionType: $expressionType',
);
}
});
}