blob: b28c3226de0c0713e810bb84f3fc9e08698dab56 [file] [log] [blame]
// Copyright (c) 2018, 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:analyzer/dart/ast/ast.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'context_collection_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(PropertyAccessResolutionTest);
defineReflectiveTests(PropertyAccessResolutionWithoutNullSafetyTest);
});
}
@reflectiveTest
class PropertyAccessResolutionTest extends PubPackageResolutionTest
with PropertyAccessResolutionTestCases {
test_implicitCall_tearOff_nullable() async {
await assertErrorsInCode('''
class A {
int call() => 0;
}
class B {
A? a;
}
int Function() foo() {
return B().a; // ref
}
''', [
error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_FUNCTION, 85, 5),
]);
var identifier = findNode.simple('a; // ref');
assertElement(identifier, findElement.getter('a'));
assertType(identifier, 'A?');
}
test_nullShorting_cascade() async {
await assertNoErrorsInCode(r'''
class A {
int get foo => 0;
int get bar => 0;
}
void f(A? a) {
a?..foo..bar;
}
''');
assertPropertyAccess2(
findNode.propertyAccess('..foo'),
element: findElement.getter('foo'),
type: 'int',
);
assertPropertyAccess2(
findNode.propertyAccess('..bar'),
element: findElement.getter('bar'),
type: 'int',
);
assertType(findNode.cascade('a?'), 'A?');
}
test_nullShorting_cascade2() async {
await assertNoErrorsInCode(r'''
class A {
int? get foo => 0;
}
main() {
A a = A()..foo?.isEven;
a;
}
''');
assertPropertyAccess2(
findNode.propertyAccess('..foo?'),
element: findElement.getter('foo'),
type: 'int?',
);
assertPropertyAccess2(
findNode.propertyAccess('.isEven'),
element: intElement.getGetter('isEven'),
type: 'bool',
);
assertType(findNode.cascade('A()'), 'A');
}
test_nullShorting_cascade3() async {
await assertNoErrorsInCode(r'''
class A {
A? get foo => this;
A? get bar => this;
A? get baz => this;
}
main() {
A a = A()..foo?.bar?.baz;
a;
}
''');
assertPropertyAccess2(
findNode.propertyAccess('.foo'),
element: findElement.getter('foo'),
type: 'A?',
);
assertPropertyAccess2(
findNode.propertyAccess('.bar'),
element: findElement.getter('bar'),
type: 'A?',
);
assertPropertyAccess2(
findNode.propertyAccess('.baz'),
element: findElement.getter('baz'),
type: 'A?',
);
assertType(findNode.cascade('A()'), 'A');
}
test_nullShorting_cascade4() async {
await assertNoErrorsInCode(r'''
A? get foo => A();
class A {
A get bar => this;
A? get baz => this;
A get baq => this;
}
main() {
foo?.bar?..baz?.baq;
}
''');
assertSimpleIdentifier(
findNode.simple('foo?'),
element: findElement.topGet('foo'),
type: 'A?',
);
assertPropertyAccess2(
findNode.propertyAccess('.bar'),
element: findElement.getter('bar'),
type: 'A?',
);
assertPropertyAccess2(
findNode.propertyAccess('.baz'),
element: findElement.getter('baz'),
type: 'A?',
);
assertPropertyAccess2(
findNode.propertyAccess('.baq'),
element: findElement.getter('baq'),
type: 'A',
);
assertType(findNode.cascade('foo?'), 'A?');
}
test_ofEnum_read() async {
await assertNoErrorsInCode('''
enum E {
v;
int get foo => 0;
}
void f(E e) {
(e).foo;
}
''');
var propertyAccess = findNode.propertyAccess('foo;');
assertPropertyAccess2(
propertyAccess,
element: findElement.getter('foo'),
type: 'int',
);
assertSimpleIdentifier(
propertyAccess.propertyName,
element: findElement.getter('foo'),
type: 'int',
);
}
test_ofEnum_read_fromMixin() async {
await assertNoErrorsInCode('''
mixin M on Enum {
int get foo => 0;
}
enum E with M {
v;
}
void f(E e) {
(e).foo;
}
''');
var propertyAccess = findNode.propertyAccess('foo;');
assertPropertyAccess2(
propertyAccess,
element: findElement.getter('foo'),
type: 'int',
);
assertSimpleIdentifier(
propertyAccess.propertyName,
element: findElement.getter('foo'),
type: 'int',
);
}
test_ofEnum_write() async {
await assertNoErrorsInCode('''
enum E {
v;
set foo(int _) {}
}
void f(E e) {
(e).foo = 1;
}
''');
var assignment = findNode.assignment('foo = 1');
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: ParenthesizedExpression
leftParenthesis: (
expression: SimpleIdentifier
token: e
staticElement: self::@function::f::@parameter::e
staticType: E
rightParenthesis: )
staticType: E
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: =
rightHandSide: IntegerLiteral
literal: 1
parameter: self::@enum::E::@setter::foo::@parameter::_
staticType: int
readElement: <null>
readType: null
writeElement: self::@enum::E::@setter::foo
writeType: int
staticElement: <null>
staticType: int
''');
}
test_ofExtension_onRecordType() async {
await assertNoErrorsInCode('''
extension IntStringRecordExtension on (int, String) {
int get foo => 0;
}
void f((int, String) r) {
r.foo;
}
''');
final node = findNode.propertyAccess('foo;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: SimpleIdentifier
token: r
staticElement: self::@function::f::@parameter::r
staticType: (int, String)
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: self::@extension::IntStringRecordExtension::@getter::foo
staticType: int
staticType: int
''');
}
test_ofExtension_onRecordType_generic() async {
await assertNoErrorsInCode('''
extension BiRecordExtension<T, U> on (T, U) {
Map<T, U> get foo => {};
}
void f((int, String) r) {
r.foo;
}
''');
final node = findNode.propertyAccess('foo;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: SimpleIdentifier
token: r
staticElement: self::@function::f::@parameter::r
staticType: (int, String)
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: PropertyAccessorMember
base: self::@extension::BiRecordExtension::@getter::foo
substitution: {T: int, U: String}
staticType: Map<int, String>
staticType: Map<int, String>
''');
}
test_ofRecordType_namedField() async {
await assertNoErrorsInCode('''
void f(({int foo}) r) {
r.foo;
}
''');
final node = findNode.propertyAccess('foo;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: SimpleIdentifier
token: r
staticElement: self::@function::f::@parameter::r
staticType: ({int foo})
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: int
staticType: int
''');
}
test_ofRecordType_namedField_hasExtension() async {
await assertNoErrorsInCode('''
extension E on ({int foo}) {
bool get foo => false;
}
void f(({int foo}) r) {
r.foo;
}
''');
final node = findNode.propertyAccess('foo;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: SimpleIdentifier
token: r
staticElement: self::@function::f::@parameter::r
staticType: ({int foo})
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: int
staticType: int
''');
}
test_ofRecordType_namedField_nullAware() async {
await assertNoErrorsInCode('''
void f(({int foo})? r) {
r?.foo;
}
''');
final node = findNode.propertyAccess('foo;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: SimpleIdentifier
token: r
staticElement: self::@function::f::@parameter::r
staticType: ({int foo})?
operator: ?.
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: int
staticType: int?
''');
}
test_ofRecordType_Object_hashCode() async {
await assertNoErrorsInCode('''
void f(({int foo}) r) {
r.hashCode;
}
''');
final node = findNode.propertyAccess('hashCode;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: SimpleIdentifier
token: r
staticElement: self::@function::f::@parameter::r
staticType: ({int foo})
operator: .
propertyName: SimpleIdentifier
token: hashCode
staticElement: dart:core::@class::Object::@getter::hashCode
staticType: int
staticType: int
''');
}
test_ofRecordType_positionalField_0() async {
await assertNoErrorsInCode(r'''
void f((int, String) r) {
r.$0;
}
''');
final node = findNode.propertyAccess(r'$0;');
assertResolvedNodeText(node, r'''
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
''');
}
test_ofRecordType_positionalField_0_hasExtension() async {
await assertNoErrorsInCode(r'''
extension E on (int, String) {
bool get $0 => false;
}
void f((int, String) r) {
r.$0;
}
''');
final node = findNode.propertyAccess(r'$0;');
assertResolvedNodeText(node, r'''
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
''');
}
test_ofRecordType_positionalField_1() async {
await assertNoErrorsInCode(r'''
void f((int, String) r) {
r.$1;
}
''');
final node = findNode.propertyAccess(r'$1;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: SimpleIdentifier
token: r
staticElement: self::@function::f::@parameter::r
staticType: (int, String)
operator: .
propertyName: SimpleIdentifier
token: $1
staticElement: <null>
staticType: String
staticType: String
''');
}
test_ofRecordType_positionalField_2_fromExtension() async {
await assertNoErrorsInCode(r'''
extension on (int, String) {
bool get $2 => false;
}
void f((int, String) r) {
r.$2;
}
''');
final node = findNode.propertyAccess(r'$2;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: SimpleIdentifier
token: r
staticElement: self::@function::f::@parameter::r
staticType: (int, String)
operator: .
propertyName: SimpleIdentifier
token: $2
staticElement: self::@extension::0::@getter::$2
staticType: bool
staticType: bool
''');
}
test_ofRecordType_positionalField_2_unresolved() async {
await assertErrorsInCode(r'''
void f((int, String) r) {
r.$2;
}
''', [
error(CompileTimeErrorCode.UNDEFINED_GETTER, 30, 2),
]);
final node = findNode.propertyAccess(r'$2;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: SimpleIdentifier
token: r
staticElement: self::@function::f::@parameter::r
staticType: (int, String)
operator: .
propertyName: SimpleIdentifier
token: $2
staticElement: <null>
staticType: dynamic
staticType: dynamic
''');
}
test_ofRecordType_positionalField_dollarDigitLetter() async {
await assertErrorsInCode(r'''
void f((int, String) r) {
r.$0a;
}
''', [
error(CompileTimeErrorCode.UNDEFINED_GETTER, 30, 3),
]);
final node = findNode.propertyAccess(r'$0a;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: SimpleIdentifier
token: r
staticElement: self::@function::f::@parameter::r
staticType: (int, String)
operator: .
propertyName: SimpleIdentifier
token: $0a
staticElement: <null>
staticType: dynamic
staticType: dynamic
''');
}
test_ofRecordType_positionalField_dollarName() async {
await assertErrorsInCode(r'''
void f((int, String) r) {
r.$zero;
}
''', [
error(CompileTimeErrorCode.UNDEFINED_GETTER, 30, 5),
]);
final node = findNode.propertyAccess(r'$zero;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: SimpleIdentifier
token: r
staticElement: self::@function::f::@parameter::r
staticType: (int, String)
operator: .
propertyName: SimpleIdentifier
token: $zero
staticElement: <null>
staticType: dynamic
staticType: dynamic
''');
}
test_ofRecordType_positionalField_letterDollarZero() async {
await assertErrorsInCode(r'''
void f((int, String) r) {
r.a$0;
}
''', [
error(CompileTimeErrorCode.UNDEFINED_GETTER, 30, 3),
]);
final node = findNode.propertyAccess(r'a$0;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: SimpleIdentifier
token: r
staticElement: self::@function::f::@parameter::r
staticType: (int, String)
operator: .
propertyName: SimpleIdentifier
token: a$0
staticElement: <null>
staticType: dynamic
staticType: dynamic
''');
}
test_ofRecordType_unresolved() async {
await assertErrorsInCode('''
void f(({int foo}) r) {
r.bar;
}
''', [
error(CompileTimeErrorCode.UNDEFINED_GETTER, 28, 3),
]);
final node = findNode.propertyAccess('bar;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: SimpleIdentifier
token: r
staticElement: self::@function::f::@parameter::r
staticType: ({int foo})
operator: .
propertyName: SimpleIdentifier
token: bar
staticElement: <null>
staticType: dynamic
staticType: dynamic
''');
}
/// Even though positional fields can have names, these names cannot be
/// used to access these fields.
test_ofRecordType_unresolved_positionalField() async {
await assertErrorsInCode('''
void f((int foo, String) r) {
r.foo;
}
''', [
error(CompileTimeErrorCode.UNDEFINED_GETTER, 34, 3),
]);
final node = findNode.propertyAccess('foo;');
assertResolvedNodeText(node, r'''
PropertyAccess
target: SimpleIdentifier
token: r
staticElement: self::@function::f::@parameter::r
staticType: (int, String)
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: dynamic
staticType: dynamic
''');
}
}
mixin PropertyAccessResolutionTestCases on PubPackageResolutionTest {
test_extensionOverride_read() async {
await assertNoErrorsInCode('''
class A {}
extension E on A {
int get foo => 0;
}
void f(A a) {
E(a).foo;
}
''');
var propertyAccess = findNode.propertyAccess('foo;');
assertPropertyAccess2(
propertyAccess,
element: findElement.getter('foo'),
type: 'int',
);
assertSimpleIdentifier(
propertyAccess.propertyName,
element: findElement.getter('foo'),
type: 'int',
);
}
test_extensionOverride_readWrite_assignment() async {
await assertNoErrorsInCode('''
class A {}
extension E on A {
int get foo => 0;
set foo(num _) {}
}
void f(A a) {
E(a).foo += 1;
}
''');
var assignment = findNode.assignment('foo += 1');
if (isNullSafetyEnabled) {
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: ExtensionOverride
extensionName: SimpleIdentifier
token: E
staticElement: self::@extension::E
staticType: null
argumentList: ArgumentList
leftParenthesis: (
arguments
SimpleIdentifier
token: a
parameter: <null>
staticElement: self::@function::f::@parameter::a
staticType: A
rightParenthesis: )
extendedType: A
staticType: null
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: +=
rightHandSide: IntegerLiteral
literal: 1
parameter: dart:core::@class::num::@method::+::@parameter::other
staticType: int
readElement: self::@extension::E::@getter::foo
readType: int
writeElement: self::@extension::E::@setter::foo
writeType: num
staticElement: dart:core::@class::num::@method::+
staticType: int
''');
} else {
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: ExtensionOverride
extensionName: SimpleIdentifier
token: E
staticElement: self::@extension::E
staticType: null
argumentList: ArgumentList
leftParenthesis: (
arguments
SimpleIdentifier
token: a
parameter: <null>
staticElement: self::@function::f::@parameter::a
staticType: A*
rightParenthesis: )
extendedType: A*
staticType: null
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: +=
rightHandSide: IntegerLiteral
literal: 1
parameter: ParameterMember
base: dart:core::@class::num::@method::+::@parameter::other
isLegacy: true
staticType: int*
readElement: self::@extension::E::@getter::foo
readType: int*
writeElement: self::@extension::E::@setter::foo
writeType: num*
staticElement: MethodMember
base: dart:core::@class::num::@method::+
isLegacy: true
staticType: int*
''');
}
}
test_extensionOverride_write() async {
await assertNoErrorsInCode('''
class A {}
extension E on A {
set foo(int _) {}
}
void f(A a) {
E(a).foo = 1;
}
''');
var assignment = findNode.assignment('foo = 1');
if (isNullSafetyEnabled) {
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: ExtensionOverride
extensionName: SimpleIdentifier
token: E
staticElement: self::@extension::E
staticType: null
argumentList: ArgumentList
leftParenthesis: (
arguments
SimpleIdentifier
token: a
parameter: <null>
staticElement: self::@function::f::@parameter::a
staticType: A
rightParenthesis: )
extendedType: A
staticType: null
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: =
rightHandSide: IntegerLiteral
literal: 1
parameter: self::@extension::E::@setter::foo::@parameter::_
staticType: int
readElement: <null>
readType: null
writeElement: self::@extension::E::@setter::foo
writeType: int
staticElement: <null>
staticType: int
''');
} else {
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: ExtensionOverride
extensionName: SimpleIdentifier
token: E
staticElement: self::@extension::E
staticType: null
argumentList: ArgumentList
leftParenthesis: (
arguments
SimpleIdentifier
token: a
parameter: <null>
staticElement: self::@function::f::@parameter::a
staticType: A*
rightParenthesis: )
extendedType: A*
staticType: null
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: =
rightHandSide: IntegerLiteral
literal: 1
parameter: self::@extension::E::@setter::foo::@parameter::_
staticType: int*
readElement: <null>
readType: null
writeElement: self::@extension::E::@setter::foo
writeType: int*
staticElement: <null>
staticType: int*
''');
}
}
test_functionType_call_read() async {
await assertNoErrorsInCode('''
void f(int Function(String) a) {
(a).call;
}
''');
assertPropertyAccess2(
findNode.propertyAccess('call;'),
element: null,
type: 'int Function(String)',
);
}
test_instanceCreation_read() async {
await assertNoErrorsInCode('''
class A {
int foo = 0;
}
void f() {
A().foo;
}
''');
var propertyAccess = findNode.propertyAccess('foo;');
assertPropertyAccess2(
propertyAccess,
element: findElement.getter('foo'),
type: 'int',
);
assertSimpleIdentifier(
propertyAccess.propertyName,
element: findElement.getter('foo'),
type: 'int',
);
}
test_instanceCreation_readWrite_assignment() async {
await assertNoErrorsInCode('''
class A {
int foo = 0;
}
void f() {
A().foo += 1;
}
''');
var assignment = findNode.assignment('foo += 1');
if (isNullSafetyEnabled) {
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A
staticElement: self::@class::A::@constructor::•
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticType: A
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: +=
rightHandSide: IntegerLiteral
literal: 1
parameter: dart:core::@class::num::@method::+::@parameter::other
staticType: int
readElement: self::@class::A::@getter::foo
readType: int
writeElement: self::@class::A::@setter::foo
writeType: int
staticElement: dart:core::@class::num::@method::+
staticType: int
''');
} else {
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A*
staticElement: self::@class::A::@constructor::•
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticType: A*
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: +=
rightHandSide: IntegerLiteral
literal: 1
parameter: ParameterMember
base: dart:core::@class::num::@method::+::@parameter::other
isLegacy: true
staticType: int*
readElement: self::@class::A::@getter::foo
readType: int*
writeElement: self::@class::A::@setter::foo
writeType: int*
staticElement: MethodMember
base: dart:core::@class::num::@method::+
isLegacy: true
staticType: int*
''');
}
}
test_instanceCreation_write() async {
await assertNoErrorsInCode('''
class A {
int foo = 0;
}
void f() {
A().foo = 1;
}
''');
var assignment = findNode.assignment('foo = 1');
if (isNullSafetyEnabled) {
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A
staticElement: self::@class::A::@constructor::•
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticType: A
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: =
rightHandSide: IntegerLiteral
literal: 1
parameter: self::@class::A::@setter::foo::@parameter::_foo
staticType: int
readElement: <null>
readType: null
writeElement: self::@class::A::@setter::foo
writeType: int
staticElement: <null>
staticType: int
''');
} else {
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A*
staticElement: self::@class::A::@constructor::•
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticType: A*
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: =
rightHandSide: IntegerLiteral
literal: 1
parameter: self::@class::A::@setter::foo::@parameter::_foo
staticType: int*
readElement: <null>
readType: null
writeElement: self::@class::A::@setter::foo
writeType: int*
staticElement: <null>
staticType: int*
''');
}
}
test_invalid_inDefaultValue_nullAware() async {
await assertInvalidTestCode('''
void f({a = b?.foo}) {}
''');
assertPropertyAccess2(
findNode.propertyAccess('?.foo'),
element: null,
type: 'dynamic',
);
}
test_invalid_inDefaultValue_nullAware2() async {
await assertInvalidTestCode('''
typedef void F({a = b?.foo});
''');
assertPropertyAccess2(
findNode.propertyAccess('?.foo'),
element: null,
type: 'dynamic',
);
}
test_invalid_inDefaultValue_nullAware_cascade() async {
await assertInvalidTestCode('''
void f({a = b?..foo}) {}
''');
assertPropertyAccess2(
findNode.propertyAccess('?..foo'),
element: null,
type: 'dynamic',
);
}
test_ofDynamic_read_hash() async {
await assertNoErrorsInCode('''
void f(dynamic a) {
(a).hash;
}
''');
var propertyAccess = findNode.propertyAccess('hash;');
assertPropertyAccess2(
propertyAccess,
element: null,
type: 'dynamic',
);
assertSimpleIdentifier(
propertyAccess.propertyName,
element: null,
type: 'dynamic',
);
}
test_ofDynamic_read_hashCode() async {
await assertNoErrorsInCode('''
void f(dynamic a) {
(a).hashCode;
}
''');
var hashCodeElement = elementMatcher(
objectElement.getGetter('hashCode'),
isLegacy: isLegacyLibrary,
);
var propertyAccess = findNode.propertyAccess('hashCode;');
assertPropertyAccess2(
propertyAccess,
element: hashCodeElement,
type: 'int',
);
assertSimpleIdentifier(
propertyAccess.propertyName,
element: hashCodeElement,
type: 'int',
);
}
test_ofDynamic_read_runtimeType() async {
await assertNoErrorsInCode('''
void f(dynamic a) {
(a).runtimeType;
}
''');
var runtimeTypeElement = elementMatcher(
objectElement.getGetter('runtimeType'),
isLegacy: isLegacyLibrary,
);
var propertyAccess = findNode.propertyAccess('runtimeType;');
assertPropertyAccess2(
propertyAccess,
element: runtimeTypeElement,
type: 'Type',
);
assertSimpleIdentifier(
propertyAccess.propertyName,
element: runtimeTypeElement,
type: 'Type',
);
}
test_ofDynamic_read_toString() async {
await assertNoErrorsInCode('''
void f(dynamic a) {
(a).toString;
}
''');
var toStringElement = elementMatcher(
objectElement.getMethod('toString'),
isLegacy: isLegacyLibrary,
);
var propertyAccess = findNode.propertyAccess('toString;');
assertPropertyAccess2(
propertyAccess,
element: toStringElement,
type: 'String Function()',
);
assertSimpleIdentifier(
propertyAccess.propertyName,
element: toStringElement,
type: 'String Function()',
);
}
test_ofExtension_read() async {
await assertNoErrorsInCode('''
class A {}
extension E on A {
int get foo => 0;
}
void f(A a) {
A().foo;
}
''');
var propertyAccess = findNode.propertyAccess('foo;');
assertPropertyAccess2(
propertyAccess,
element: findElement.getter('foo'),
type: 'int',
);
assertSimpleIdentifier(
propertyAccess.propertyName,
element: findElement.getter('foo'),
type: 'int',
);
}
test_ofExtension_readWrite_assignment() async {
await assertNoErrorsInCode('''
class A {}
extension E on A {
int get foo => 0;
set foo(num _) {}
}
void f() {
A().foo += 1;
}
''');
var assignment = findNode.assignment('foo += 1');
if (isNullSafetyEnabled) {
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A
staticElement: self::@class::A::@constructor::•
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticType: A
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: +=
rightHandSide: IntegerLiteral
literal: 1
parameter: dart:core::@class::num::@method::+::@parameter::other
staticType: int
readElement: self::@extension::E::@getter::foo
readType: int
writeElement: self::@extension::E::@setter::foo
writeType: num
staticElement: dart:core::@class::num::@method::+
staticType: int
''');
} else {
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A*
staticElement: self::@class::A::@constructor::•
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticType: A*
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: +=
rightHandSide: IntegerLiteral
literal: 1
parameter: ParameterMember
base: dart:core::@class::num::@method::+::@parameter::other
isLegacy: true
staticType: int*
readElement: self::@extension::E::@getter::foo
readType: int*
writeElement: self::@extension::E::@setter::foo
writeType: num*
staticElement: MethodMember
base: dart:core::@class::num::@method::+
isLegacy: true
staticType: int*
''');
}
}
test_ofExtension_write() async {
await assertNoErrorsInCode('''
class A {}
extension E on A {
set foo(int _) {}
}
void f() {
A().foo = 1;
}
''');
var assignment = findNode.assignment('foo = 1');
if (isNullSafetyEnabled) {
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A
staticElement: self::@class::A::@constructor::•
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticType: A
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: =
rightHandSide: IntegerLiteral
literal: 1
parameter: self::@extension::E::@setter::foo::@parameter::_
staticType: int
readElement: <null>
readType: null
writeElement: self::@extension::E::@setter::foo
writeType: int
staticElement: <null>
staticType: int
''');
} else {
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A*
staticElement: self::@class::A::@constructor::•
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticType: A*
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: =
rightHandSide: IntegerLiteral
literal: 1
parameter: self::@extension::E::@setter::foo::@parameter::_
staticType: int*
readElement: <null>
readType: null
writeElement: self::@extension::E::@setter::foo
writeType: int*
staticElement: <null>
staticType: int*
''');
}
}
test_super_read() async {
await assertNoErrorsInCode('''
class A {
int foo = 0;
}
class B extends A {
void f() {
super.foo;
}
}
''');
var propertyAccess = findNode.propertyAccess('super.foo');
assertPropertyAccess2(
propertyAccess,
element: findElement.getter('foo'),
type: 'int',
);
assertSuperExpression(
propertyAccess.target,
);
assertSimpleIdentifier(
propertyAccess.propertyName,
element: findElement.getter('foo'),
type: 'int',
);
}
test_super_readWrite_assignment() async {
await assertNoErrorsInCode('''
class A {
int foo = 0;
}
class B extends A {
void f() {
super.foo += 1;
}
}
''');
var assignment = findNode.assignment('foo += 1');
if (isNullSafetyEnabled) {
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: SuperExpression
superKeyword: super
staticType: B
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: +=
rightHandSide: IntegerLiteral
literal: 1
parameter: dart:core::@class::num::@method::+::@parameter::other
staticType: int
readElement: self::@class::A::@getter::foo
readType: int
writeElement: self::@class::A::@setter::foo
writeType: int
staticElement: dart:core::@class::num::@method::+
staticType: int
''');
} else {
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: SuperExpression
superKeyword: super
staticType: B*
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: +=
rightHandSide: IntegerLiteral
literal: 1
parameter: ParameterMember
base: dart:core::@class::num::@method::+::@parameter::other
isLegacy: true
staticType: int*
readElement: self::@class::A::@getter::foo
readType: int*
writeElement: self::@class::A::@setter::foo
writeType: int*
staticElement: MethodMember
base: dart:core::@class::num::@method::+
isLegacy: true
staticType: int*
''');
}
var propertyAccess = assignment.leftHandSide as PropertyAccess;
assertSuperExpression(
propertyAccess.target,
);
}
test_super_write() async {
await assertNoErrorsInCode('''
class A {
int foo = 0;
}
class B extends A {
void f() {
super.foo = 1;
}
}
''');
var assignment = findNode.assignment('foo = 1');
if (isNullSafetyEnabled) {
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: SuperExpression
superKeyword: super
staticType: B
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: =
rightHandSide: IntegerLiteral
literal: 1
parameter: self::@class::A::@setter::foo::@parameter::_foo
staticType: int
readElement: <null>
readType: null
writeElement: self::@class::A::@setter::foo
writeType: int
staticElement: <null>
staticType: int
''');
} else {
assertResolvedNodeText(assignment, r'''
AssignmentExpression
leftHandSide: PropertyAccess
target: SuperExpression
superKeyword: super
staticType: B*
operator: .
propertyName: SimpleIdentifier
token: foo
staticElement: <null>
staticType: null
staticType: null
operator: =
rightHandSide: IntegerLiteral
literal: 1
parameter: self::@class::A::@setter::foo::@parameter::_foo
staticType: int*
readElement: <null>
readType: null
writeElement: self::@class::A::@setter::foo
writeType: int*
staticElement: <null>
staticType: int*
''');
}
var propertyAccess = assignment.leftHandSide as PropertyAccess;
assertSuperExpression(
propertyAccess.target,
);
}
test_targetTypeParameter_dynamicBounded() async {
await assertNoErrorsInCode('''
class A<T extends dynamic> {
void f(T t) {
(t).foo;
}
}
''');
var propertyAccess = findNode.propertyAccess('.foo');
assertPropertyAccess2(
propertyAccess,
element: null,
type: 'dynamic',
);
assertSimpleIdentifier(
propertyAccess.propertyName,
element: null,
type: 'dynamic',
);
}
test_targetTypeParameter_noBound() async {
await resolveTestCode('''
class C<T> {
void f(T t) {
(t).foo;
}
}
''');
assertErrorsInResult(expectedErrorsByNullability(
nullable: [
error(CompileTimeErrorCode.UNCHECKED_PROPERTY_ACCESS_OF_NULLABLE_VALUE,
37, 3),
],
legacy: [
error(CompileTimeErrorCode.UNDEFINED_GETTER, 37, 3),
],
));
var propertyAccess = findNode.propertyAccess('.foo');
assertPropertyAccess2(
propertyAccess,
element: null,
type: 'dynamic',
);
assertSimpleIdentifier(
propertyAccess.propertyName,
element: null,
type: 'dynamic',
);
}
test_tearOff_method() async {
await assertNoErrorsInCode('''
class A {
void foo(int a) {}
}
bar() {
A().foo;
}
''');
var identifier = findNode.simple('foo;');
assertElement(identifier, findElement.method('foo'));
assertType(identifier, 'void Function(int)');
}
}
@reflectiveTest
class PropertyAccessResolutionWithoutNullSafetyTest
extends PubPackageResolutionTest
with PropertyAccessResolutionTestCases, WithoutNullSafetyMixin {}