blob: 8223fcfe914180c6038ee9b5dc51ffbbf272fcb9 [file] [log] [blame]
// Copyright (c) 2022, 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:test_reflective_loader/test_reflective_loader.dart';
import 'context_collection_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(FieldPromotionTest);
});
}
@reflectiveTest
class FieldPromotionTest extends PubPackageResolutionTest {
test_class_field_invocation_prefixedIdentifier_nullability() async {
await assertNoErrorsInCode('''
class C {
final void Function()? _foo;
C(this._foo);
}
void f(C c) {
if (c._foo != null) {
c._foo();
}
}
''');
var node = findNode.functionExpressionInvocation('_foo()');
assertResolvedNodeText(node, r'''
FunctionExpressionInvocation
function: PropertyAccess
target: SimpleIdentifier
token: c
staticElement: self::@function::f::@parameter::c
staticType: C
operator: .
propertyName: SimpleIdentifier
token: _foo
staticElement: self::@class::C::@getter::_foo
staticType: void Function()
staticType: void Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticElement: <null>
staticInvokeType: void Function()
staticType: void
''');
}
test_class_field_invocation_prefixedIdentifier_returnType() async {
await assertNoErrorsInCode('''
class C {
final int? Function() _foo;
C(this._foo);
}
void f(C c) {
if (c._foo is int Function()) {
c._foo();
}
}
''');
var node = findNode.functionExpressionInvocation('_foo()');
assertResolvedNodeText(node, r'''
FunctionExpressionInvocation
function: PropertyAccess
target: SimpleIdentifier
token: c
staticElement: self::@function::f::@parameter::c
staticType: C
operator: .
propertyName: SimpleIdentifier
token: _foo
staticElement: self::@class::C::@getter::_foo
staticType: int Function()
staticType: int Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticElement: <null>
staticInvokeType: int Function()
staticType: int
''');
}
test_class_field_invocation_propertyAccess_nullability() async {
await assertNoErrorsInCode('''
class C {
final void Function()? _foo;
C(this._foo);
}
void f(C c) {
if ((c)._foo != null) {
(c)._foo();
}
}
''');
var node = findNode.functionExpressionInvocation('_foo()');
assertResolvedNodeText(node, r'''
FunctionExpressionInvocation
function: PropertyAccess
target: ParenthesizedExpression
leftParenthesis: (
expression: SimpleIdentifier
token: c
staticElement: self::@function::f::@parameter::c
staticType: C
rightParenthesis: )
staticType: C
operator: .
propertyName: SimpleIdentifier
token: _foo
staticElement: self::@class::C::@getter::_foo
staticType: void Function()
staticType: void Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticElement: <null>
staticInvokeType: void Function()
staticType: void
''');
}
test_class_field_invocation_propertyAccess_returnType() async {
await assertNoErrorsInCode('''
class C {
final int? Function() _foo;
C(this._foo);
}
void f(C c) {
if ((c)._foo is int Function()) {
(c)._foo();
}
}
''');
var node = findNode.functionExpressionInvocation('_foo()');
assertResolvedNodeText(node, r'''
FunctionExpressionInvocation
function: PropertyAccess
target: ParenthesizedExpression
leftParenthesis: (
expression: SimpleIdentifier
token: c
staticElement: self::@function::f::@parameter::c
staticType: C
rightParenthesis: )
staticType: C
operator: .
propertyName: SimpleIdentifier
token: _foo
staticElement: self::@class::C::@getter::_foo
staticType: int Function()
staticType: int Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticElement: <null>
staticInvokeType: int Function()
staticType: int
''');
}
test_class_field_invocation_simpleIdentifier_nullability() async {
await assertNoErrorsInCode('''
class C {
final void Function()? _foo;
C(this._foo);
}
class D extends C {
D(super.value);
void f() {
if (_foo != null) {
_foo();
}
}
}
''');
var node = findNode.functionExpressionInvocation('_foo()');
assertResolvedNodeText(node, r'''
FunctionExpressionInvocation
function: SimpleIdentifier
token: _foo
staticElement: self::@class::C::@getter::_foo
staticType: void Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticElement: <null>
staticInvokeType: void Function()
staticType: void
''');
}
test_class_field_invocation_simpleIdentifier_returnType() async {
await assertNoErrorsInCode('''
class C {
final int? Function() _foo;
C(this._foo);
}
class D extends C {
D(super.value);
void f() {
if (_foo is int Function()) {
_foo();
}
}
}
''');
var node = findNode.functionExpressionInvocation('_foo()');
assertResolvedNodeText(node, r'''
FunctionExpressionInvocation
function: SimpleIdentifier
token: _foo
staticElement: self::@class::C::@getter::_foo
staticType: int Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticElement: <null>
staticInvokeType: int Function()
staticType: int
''');
}
test_class_field_invocation_superPropertyAccess_nullability() async {
await assertNoErrorsInCode('''
class C {
final void Function()? _foo;
C(this._foo);
}
class D extends C {
D(super.value);
void f() {
if (super._foo != null) {
super._foo();
}
}
}
''');
var node = findNode.functionExpressionInvocation('_foo()');
assertResolvedNodeText(node, r'''
FunctionExpressionInvocation
function: PropertyAccess
target: SuperExpression
superKeyword: super
staticType: D
operator: .
propertyName: SimpleIdentifier
token: _foo
staticElement: self::@class::C::@getter::_foo
staticType: void Function()
staticType: void Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticElement: <null>
staticInvokeType: void Function()
staticType: void
''');
}
test_class_field_invocation_superPropertyAccess_returnType() async {
await assertNoErrorsInCode('''
class C {
final int? Function() _foo;
C(this._foo);
}
class D extends C {
D(super.value);
void f() {
if (super._foo is int Function()) {
super._foo();
}
}
}
''');
var node = findNode.functionExpressionInvocation('_foo()');
assertResolvedNodeText(node, r'''
FunctionExpressionInvocation
function: PropertyAccess
target: SuperExpression
superKeyword: super
staticType: D
operator: .
propertyName: SimpleIdentifier
token: _foo
staticElement: self::@class::C::@getter::_foo
staticType: int Function()
staticType: int Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticElement: <null>
staticInvokeType: int Function()
staticType: int
''');
}
test_class_field_notFinal() async {
await assertNoErrorsInCode('''
class C {
int? _foo;
C(this._foo);
}
void f(C c) {
if (c._foo != null) {
c._foo;
}
}
''');
var node = findNode.prefixed('c._foo;');
assertResolvedNodeText(node, r'''
PrefixedIdentifier
prefix: SimpleIdentifier
token: c
staticElement: self::@function::f::@parameter::c
staticType: C
period: .
identifier: SimpleIdentifier
token: _foo
staticElement: self::@class::C::@getter::_foo
staticType: int?
staticElement: self::@class::C::@getter::_foo
staticType: int?
''');
}
test_class_field_notPrivate() async {
await assertNoErrorsInCode('''
class C {
int? foo;
C(this.foo);
}
void f(C c) {
if (c.foo != null) {
c.foo;
}
}
''');
var node = findNode.prefixed('.foo;');
assertResolvedNodeText(node, r'''
PrefixedIdentifier
prefix: SimpleIdentifier
token: c
staticElement: self::@function::f::@parameter::c
staticType: C
period: .
identifier: SimpleIdentifier
token: foo
staticElement: self::@class::C::@getter::foo
staticType: int?
staticElement: self::@class::C::@getter::foo
staticType: int?
''');
}
test_class_field_read_prefixedIdentifier() async {
await assertNoErrorsInCode('''
class C {
final int? _foo;
C(this._foo);
}
void f(C c) {
if (c._foo != null) {
c._foo;
}
}
''');
var node = findNode.prefixed('c._foo;');
assertResolvedNodeText(node, r'''
PrefixedIdentifier
prefix: SimpleIdentifier
token: c
staticElement: self::@function::f::@parameter::c
staticType: C
period: .
identifier: SimpleIdentifier
token: _foo
staticElement: self::@class::C::@getter::_foo
staticType: int
staticElement: self::@class::C::@getter::_foo
staticType: int
''');
}
test_class_field_read_propertyAccess() async {
await assertNoErrorsInCode('''
class C {
final int? _foo;
C(this._foo);
}
void f(C c) {
if ((c)._foo != null) {
(c)._foo;
}
}
''');
var node = findNode.propertyAccess('._foo;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: ParenthesizedExpression
leftParenthesis: (
expression: SimpleIdentifier
token: c
staticElement: self::@function::f::@parameter::c
staticType: C
rightParenthesis: )
staticType: C
operator: .
propertyName: SimpleIdentifier
token: _foo
staticElement: self::@class::C::@getter::_foo
staticType: int
staticType: int
''');
}
test_class_field_read_propertyAccess_super() async {
await assertNoErrorsInCode('''
class C {
final int? _foo;
C(this._foo);
}
class D extends C {
D(super.value);
void f() {
if (super._foo != null) {
super._foo;
}
}
}
''');
var node = findNode.propertyAccess('._foo;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: SuperExpression
superKeyword: super
staticType: D
operator: .
propertyName: SimpleIdentifier
token: _foo
staticElement: self::@class::C::@getter::_foo
staticType: int
staticType: int
''');
}
test_class_field_read_simpleIdentifier() async {
await assertNoErrorsInCode('''
class C {
final int? _foo;
C(this._foo);
void f() {
if (_foo != null) {
_foo; // read
}
}
}
''');
var node = findNode.simple('_foo; // read');
assertResolvedNodeText(node, r'''
SimpleIdentifier
token: _foo
staticElement: self::@class::C::@getter::_foo
staticType: int
''');
}
test_class_getter_read() async {
await assertNoErrorsInCode('''
abstract class C {
int? get _foo;
}
void f(C c) {
if (c._foo != null) {
c._foo;
}
}
''');
var node = findNode.prefixed('c._foo;');
assertResolvedNodeText(node, r'''
PrefixedIdentifier
prefix: SimpleIdentifier
token: c
staticElement: self::@function::f::@parameter::c
staticType: C
period: .
identifier: SimpleIdentifier
token: _foo
staticElement: self::@class::C::@getter::_foo
staticType: int?
staticElement: self::@class::C::@getter::_foo
staticType: int?
''');
}
test_classTypeAlias_construction() async {
// In an earlier implementation attempt, field promotability was computed
// prior to supertypes to class type aliases. As a result, the class type
// aliases' `supertype` fields were frozen at `null`, and consequently,
// synthetic constructors weren't properly built for them, leading to bogus
// error messages when constructing them. This is a regression test to
// ensure that mistake doesn't happen again.
await assertNoErrorsInCode('''
mixin M {
// ignore:unused_field
int? _x = 43;
}
class C = Object with M;
void f() {
C();
}
''');
}
test_enum_field() async {
await assertNoErrorsInCode('''
enum E {
v(null);
final int? _foo;
const E(this._foo);
}
void f(E e) {
if (e._foo != null) {
e._foo;
}
}
''');
var node = findNode.prefixed('._foo;');
assertResolvedNodeText(node, r'''
PrefixedIdentifier
prefix: SimpleIdentifier
token: e
staticElement: self::@function::f::@parameter::e
staticType: E
period: .
identifier: SimpleIdentifier
token: _foo
staticElement: self::@enum::E::@getter::_foo
staticType: int
staticElement: self::@enum::E::@getter::_foo
staticType: int
''');
}
test_language218() async {
await assertNoErrorsInCode('''
// @dart=2.18
class C {
final int? _foo;
C(this._foo);
}
void f(C c) {
if ((c)._foo != null) {
(c)._foo;
}
}
''');
var node = findNode.propertyAccess('._foo;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: ParenthesizedExpression
leftParenthesis: (
expression: SimpleIdentifier
token: c
staticElement: self::@function::f::@parameter::c
staticType: C
rightParenthesis: )
staticType: C
operator: .
propertyName: SimpleIdentifier
token: _foo
staticElement: self::@class::C::@getter::_foo
staticType: int?
staticType: int?
''');
}
}