blob: 00822e8f813352317bc10d12262ab82fc1f20829 [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';
import 'node_text_expectations.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(FieldPromotionTest);
defineReflectiveTests(UpdateNodeTextExpectations);
});
}
@reflectiveTest
class FieldPromotionTest extends PubPackageResolutionTest {
test_cascaded_invocation() async {
await assertNoErrorsInCode('''
class C {
final Object? _field;
C(this._field);
}
void f(C c) {
c._field as int Function();
c.._field().toString();
}
''');
var node = findNode.functionExpressionInvocation('_field()');
assertResolvedNodeText(node, r'''
FunctionExpressionInvocation
function: SimpleIdentifier
token: _field
element: <testLibraryFragment>::@class::C::@getter::_field#element
staticType: int Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <null>
staticInvokeType: int Function()
staticType: int
''');
}
test_cascaded_propertyAccess() async {
await assertNoErrorsInCode('''
class C {
final Object? _field;
C(this._field);
}
void f(C c) {
c._field as int;
c.._field.toString();
}
''');
var node = findNode.methodInvocation('_field.toString');
assertResolvedNodeText(node, r'''
MethodInvocation
target: PropertyAccess
operator: ..
propertyName: SimpleIdentifier
token: _field
element: <testLibraryFragment>::@class::C::@getter::_field#element
staticType: int
staticType: int
operator: .
methodName: SimpleIdentifier
token: toString
element: dart:core::@class::int::@method::toString
staticType: String Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticInvokeType: String Function()
staticType: String
''');
}
test_cascaded_propertyAccess_nullAware() async {
await assertNoErrorsInCode('''
class C {
final Object? _field;
C(this._field);
}
void f(C? c) {
c?.._field!.toString().._field.toString();
c?._field;
}
''');
// The `!` in the first statement promotes _field within the cascade
assertResolvedNodeText(findNode.propertyAccess('_field.toString'), r'''
PropertyAccess
operator: ..
propertyName: SimpleIdentifier
token: _field
element: <testLibraryFragment>::@class::C::@getter::_field#element
staticType: Object
staticType: Object
''');
// But the promotion doesn't last beyond the cascade expression, due to the
// implicit control flow join when the `?..` stops taking effect.
assertResolvedNodeText(findNode.propertyAccess('c?._field'), r'''
PropertyAccess
target: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C?
operator: ?.
propertyName: SimpleIdentifier
token: _field
element: <testLibraryFragment>::@class::C::@getter::_field#element
staticType: Object?
staticType: Object?
''');
}
test_class_field_abstract() async {
// Even though an abstract non-final field is just syntactic sugar for an
// abstract getter/setter pair (and thus in principle shouldn't prevent
// promotion), there's no way to implement it without introducing either a
// getter or a non-final field (either of which would prevent promotion). So
// the implementation goes ahead and prevents promotion even if there's no
// implementation yet, to reduce churn for the user.
await assertNoErrorsInCode('''
abstract class B {
abstract int? _foo;
}
// Suppress "unused field" warning on `B._foo`.
int? f(B b) => b._foo;
class C {
final int? _foo;
C(this._foo);
}
void g(C c) {
if (c._foo != null) {
c._foo;
}
}
''');
var node = findNode.prefixed('c._foo;');
assertResolvedNodeText(node, r'''
PrefixedIdentifier
prefix: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::g::@parameter::c#element
staticType: C
period: .
identifier: SimpleIdentifier
token: _foo
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: int?
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: int?
''');
}
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
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
operator: .
propertyName: SimpleIdentifier
token: _foo
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: void Function()
staticType: void Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <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
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
operator: .
propertyName: SimpleIdentifier
token: _foo
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: int Function()
staticType: int Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <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
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
rightParenthesis: )
staticType: C
operator: .
propertyName: SimpleIdentifier
token: _foo
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: void Function()
staticType: void Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <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
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
rightParenthesis: )
staticType: C
operator: .
propertyName: SimpleIdentifier
token: _foo
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: int Function()
staticType: int Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <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
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: void Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <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
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: int Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <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
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: void Function()
staticType: void Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <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
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: int Function()
staticType: int Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <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
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
period: .
identifier: SimpleIdentifier
token: _foo
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: int?
element: <testLibraryFragment>::@class::C::@getter::_foo#element
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
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
period: .
identifier: SimpleIdentifier
token: foo
element: <testLibraryFragment>::@class::C::@getter::foo#element
staticType: int?
element: <testLibraryFragment>::@class::C::@getter::foo#element
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
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
period: .
identifier: SimpleIdentifier
token: _foo
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: int
element: <testLibraryFragment>::@class::C::@getter::_foo#element
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
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
rightParenthesis: )
staticType: C
operator: .
propertyName: SimpleIdentifier
token: _foo
element: <testLibraryFragment>::@class::C::@getter::_foo#element
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
element: <testLibraryFragment>::@class::C::@getter::_foo#element
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
element: <testLibraryFragment>::@class::C::@getter::_foo#element
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
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
period: .
identifier: SimpleIdentifier
token: _foo
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: int
element: <testLibraryFragment>::@class::C::@getter::_foo#element
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
element: <testLibraryFragment>::@function::f::@parameter::e#element
staticType: E
period: .
identifier: SimpleIdentifier
token: _foo
element: <testLibraryFragment>::@enum::E::@getter::_foo#element
staticType: int
element: <testLibraryFragment>::@enum::E::@getter::_foo#element
staticType: int
''');
}
test_extensionType_field_representation() async {
await assertNoErrorsInCode('''
extension type A(int? _it) {}
void f(A a) {
if (a._it != null) {
a._it;
}
}
''');
var node = findNode.prefixed('a._it;');
assertResolvedNodeText(node, r'''
PrefixedIdentifier
prefix: SimpleIdentifier
token: a
element: <testLibraryFragment>::@function::f::@parameter::a#element
staticType: A
period: .
identifier: SimpleIdentifier
token: _it
element: <testLibraryFragment>::@extensionType::A::@getter::_it#element
staticType: int
element: <testLibraryFragment>::@extensionType::A::@getter::_it#element
staticType: int
''');
}
test_external_field() async {
// External final fields should not be promotable.
await assertNoErrorsInCode('''
class C {
external final int? _field;
}
void f(C c) {
c._field!;
c._field;
}
''');
var node = findNode.prefixed('c._field;');
assertResolvedNodeText(node, r'''
PrefixedIdentifier
prefix: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
period: .
identifier: SimpleIdentifier
token: _field
element: <testLibraryFragment>::@class::C::@getter::_field#element
staticType: int?
element: <testLibraryFragment>::@class::C::@getter::_field#element
staticType: int?
''');
}
test_implemented_via_other_library() async {
// When determining the set of fields/getters in a class's implementation,
// it's necessary to traverse the whole class hierarchy, including classes
// outside the current library, because a class outside the current library
// may extend a class inside the current library. In the example below,
// `c._foo` is promotable because class E doesn't contain any `noSuchMethod`
// getters, due to the fact that it inherits an implementation of `_foo`
// from `C` via `D`.
newFile('$testPackageLibPath/other.dart', '''
import 'test.dart';
class D extends C {
D(super.foo);
}
''');
await assertNoErrorsInCode('''
import 'other.dart';
class C {
final int? _foo;
C(this._foo);
}
class E extends D implements C {
E(super.foo);
noSuchMethod(_) => 12345;
}
void f(C c) {
if (c._foo != null) {
c._foo;
}
}
''');
var node = findNode.prefixed('c._foo;');
assertResolvedNodeText(node, r'''
PrefixedIdentifier
prefix: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
period: .
identifier: SimpleIdentifier
token: _foo
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: int
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: int
''');
}
test_interface_via_other_library() async {
// When determining the set of fields/getters in a class's interface, it's
// necessary to traverse the whole class hierarchy, including classes
// outside the current library, because a class outside the current library
// may extend or implement a class inside the current library. In the
// example below, `c._foo` is not promotable because class E contains a
// `noSuchMethod` getter for `_foo`, due to the fact that its interface
// inherits `_foo` from `C` via `D`.
newFile('$testPackageLibPath/other.dart', '''
import 'test.dart';
class D extends C {
D(super.foo);
}
''');
await assertNoErrorsInCode('''
import 'other.dart';
class C {
final int? _foo;
C(this._foo);
}
class E implements D {
noSuchMethod(_) => 12345;
}
void f(C c) {
if (c._foo != null) {
c._foo;
}
}
''');
var node = findNode.prefixed('c._foo;');
assertResolvedNodeText(node, r'''
PrefixedIdentifier
prefix: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
period: .
identifier: SimpleIdentifier
token: _foo
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: int?
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: int?
''');
}
test_language219() async {
await assertNoErrorsInCode('''
// @dart = 2.19
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
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
rightParenthesis: )
staticType: C
operator: .
propertyName: SimpleIdentifier
token: _foo
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: int?
staticType: int?
''');
}
test_mixin_on_clause() async {
// The type mentioned in a a mixin's "on" clause contributes to its
// interface. This needs to be accounted for when determining whether a
// `noSuchMethod` getter will be synthesized. In the example below,
// `c._foo` is not promotable because class D contains a `noSuchMethod`
// getter for `_foo`.
await assertNoErrorsInCode('''
mixin M on C {}
class C {
final int? _foo;
C(this._foo);
}
class D implements M {
noSuchMethod(_) => 12345;
}
void f(C c) {
if (c._foo != null) {
c._foo;
}
}
''');
var node = findNode.prefixed('c._foo;');
assertResolvedNodeText(node, r'''
PrefixedIdentifier
prefix: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
period: .
identifier: SimpleIdentifier
token: _foo
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: int?
element: <testLibraryFragment>::@class::C::@getter::_foo#element
staticType: int?
''');
}
test_super_get() async {
await assertNoErrorsInCode('''
class B {
final int? _i;
B(this._i);
}
class C extends B {
final int? _i;
C(this._i, int? superI) : super(superI);
void f() {
if (_i != null) { // A
_i;
super._i;
}
if (super._i != null) { // B
_i;
super._i;
}
}
}
''');
var blockA = findNode.block('// A');
assertResolvedNodeText(blockA, r'''
Block
leftBracket: {
statements
ExpressionStatement
expression: SimpleIdentifier
token: _i
element: <testLibraryFragment>::@class::C::@getter::_i#element
staticType: int
semicolon: ;
ExpressionStatement
expression: PropertyAccess
target: SuperExpression
superKeyword: super
staticType: C
operator: .
propertyName: SimpleIdentifier
token: _i
element: <testLibraryFragment>::@class::B::@getter::_i#element
staticType: int?
staticType: int?
semicolon: ;
rightBracket: }
''');
var blockB = findNode.block('// B');
assertResolvedNodeText(blockB, r'''
Block
leftBracket: {
statements
ExpressionStatement
expression: SimpleIdentifier
token: _i
element: <testLibraryFragment>::@class::C::@getter::_i#element
staticType: int?
semicolon: ;
ExpressionStatement
expression: PropertyAccess
target: SuperExpression
superKeyword: super
staticType: C
operator: .
propertyName: SimpleIdentifier
token: _i
element: <testLibraryFragment>::@class::B::@getter::_i#element
staticType: int
staticType: int
semicolon: ;
rightBracket: }
''');
}
test_super_get_inGenericClass() async {
await assertNoErrorsInCode('''
class B<T extends Object> {
final T? _t;
B(this._t);
}
class C<T extends Object> extends B<T> {
final T? _t;
C(this._t, T? superT) : super(superT);
void f() {
if (_t != null) { // A
_t;
super._t;
}
if (super._t != null) { // B
_t;
super._t;
}
}
}
''');
var blockA = findNode.block('// A');
assertResolvedNodeText(blockA, r'''
Block
leftBracket: {
statements
ExpressionStatement
expression: SimpleIdentifier
token: _t
element: <testLibraryFragment>::@class::C::@getter::_t#element
staticType: T
semicolon: ;
ExpressionStatement
expression: PropertyAccess
target: SuperExpression
superKeyword: super
staticType: C<T>
operator: .
propertyName: SimpleIdentifier
token: _t
element: GetterMember
baseElement: <testLibraryFragment>::@class::B::@getter::_t#element
substitution: {T: T}
staticType: T?
staticType: T?
semicolon: ;
rightBracket: }
''');
var blockB = findNode.block('// B');
assertResolvedNodeText(blockB, r'''
Block
leftBracket: {
statements
ExpressionStatement
expression: SimpleIdentifier
token: _t
element: <testLibraryFragment>::@class::C::@getter::_t#element
staticType: T?
semicolon: ;
ExpressionStatement
expression: PropertyAccess
target: SuperExpression
superKeyword: super
staticType: C<T>
operator: .
propertyName: SimpleIdentifier
token: _t
element: GetterMember
baseElement: <testLibraryFragment>::@class::B::@getter::_t#element
substitution: {T: T}
staticType: T
staticType: T
semicolon: ;
rightBracket: }
''');
}
test_super_getAndInvoke() async {
await assertNoErrorsInCode('''
class B {
final int? Function() _f;
B(this._f);
}
class C extends B {
final int? Function() _f;
C(this._f, int? Function() superF) : super(superF);
void f() {
if (_f is int Function()) { // A
_f();
super._f();
}
if (super._f is int Function()) { // B
_f();
super._f();
}
}
}
''');
var blockA = findNode.block('// A');
assertResolvedNodeText(blockA, r'''
Block
leftBracket: {
statements
ExpressionStatement
expression: FunctionExpressionInvocation
function: SimpleIdentifier
token: _f
element: <testLibraryFragment>::@class::C::@getter::_f#element
staticType: int Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <null>
staticInvokeType: int Function()
staticType: int
semicolon: ;
ExpressionStatement
expression: FunctionExpressionInvocation
function: PropertyAccess
target: SuperExpression
superKeyword: super
staticType: C
operator: .
propertyName: SimpleIdentifier
token: _f
element: <testLibraryFragment>::@class::B::@getter::_f#element
staticType: int? Function()
staticType: int? Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <null>
staticInvokeType: int? Function()
staticType: int?
semicolon: ;
rightBracket: }
''');
var blockB = findNode.block('// B');
assertResolvedNodeText(blockB, r'''
Block
leftBracket: {
statements
ExpressionStatement
expression: FunctionExpressionInvocation
function: SimpleIdentifier
token: _f
element: <testLibraryFragment>::@class::C::@getter::_f#element
staticType: int? Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <null>
staticInvokeType: int? Function()
staticType: int?
semicolon: ;
ExpressionStatement
expression: FunctionExpressionInvocation
function: PropertyAccess
target: SuperExpression
superKeyword: super
staticType: C
operator: .
propertyName: SimpleIdentifier
token: _f
element: <testLibraryFragment>::@class::B::@getter::_f#element
staticType: int Function()
staticType: int Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <null>
staticInvokeType: int Function()
staticType: int
semicolon: ;
rightBracket: }
''');
}
test_super_getAndInvoke_inGenericClass() async {
await assertNoErrorsInCode('''
class B<T extends Object> {
final T? Function() _f;
B(this._f);
}
class C<T extends Object> extends B<T> {
final T? Function() _f;
C(this._f, T? Function() superF) : super(superF);
void f() {
if (_f is T Function()) { // A
_f();
super._f();
}
if (super._f is T Function()) { // B
_f();
super._f();
}
}
}
''');
var blockA = findNode.block('// A');
assertResolvedNodeText(blockA, r'''
Block
leftBracket: {
statements
ExpressionStatement
expression: FunctionExpressionInvocation
function: SimpleIdentifier
token: _f
element: <testLibraryFragment>::@class::C::@getter::_f#element
staticType: T Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <null>
staticInvokeType: T Function()
staticType: T
semicolon: ;
ExpressionStatement
expression: FunctionExpressionInvocation
function: PropertyAccess
target: SuperExpression
superKeyword: super
staticType: C<T>
operator: .
propertyName: SimpleIdentifier
token: _f
element: GetterMember
baseElement: <testLibraryFragment>::@class::B::@getter::_f#element
substitution: {T: T}
staticType: T? Function()
staticType: T? Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <null>
staticInvokeType: T? Function()
staticType: T?
semicolon: ;
rightBracket: }
''');
var blockB = findNode.block('// B');
assertResolvedNodeText(blockB, r'''
Block
leftBracket: {
statements
ExpressionStatement
expression: FunctionExpressionInvocation
function: SimpleIdentifier
token: _f
element: <testLibraryFragment>::@class::C::@getter::_f#element
staticType: T? Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <null>
staticInvokeType: T? Function()
staticType: T?
semicolon: ;
ExpressionStatement
expression: FunctionExpressionInvocation
function: PropertyAccess
target: SuperExpression
superKeyword: super
staticType: C<T>
operator: .
propertyName: SimpleIdentifier
token: _f
element: GetterMember
baseElement: <testLibraryFragment>::@class::B::@getter::_f#element
substitution: {T: T}
staticType: T Function()
staticType: T Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
element: <null>
staticInvokeType: T Function()
staticType: T
semicolon: ;
rightBracket: }
''');
}
}