blob: 4d8bbcf5d1e5066a3fc59853d71fd52221c744f0 [file] [log] [blame]
// Copyright (c) 2019, 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/src/error/codes.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'context_collection_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(AstRewriteImplicitCallReferenceTest);
defineReflectiveTests(AstRewriteMethodInvocationTest);
defineReflectiveTests(AstRewritePrefixedIdentifierTest);
// TODO(srawlins): Add AstRewriteInstanceCreationExpressionTest test, likely
// moving many test cases from ConstructorReferenceResolutionTest,
// FunctionReferenceResolutionTest, and TypeLiteralResolutionTest.
// TODO(srawlins): Add AstRewritePropertyAccessTest test, likely
// moving many test cases from ConstructorReferenceResolutionTest,
// FunctionReferenceResolutionTest, and TypeLiteralResolutionTest.
});
}
@reflectiveTest
class AstRewriteImplicitCallReferenceTest extends PubPackageResolutionTest {
test_assignment_indexExpression() async {
await assertNoErrorsInCode('''
abstract class C {
void call(int t) => t;
}
void Function(int) foo(C c) {
var map = <int, C>{};
return map[1] = c;
}
''');
var node = findNode.implicitCallReference('c;');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: AssignmentExpression
leftHandSide: IndexExpression
target: SimpleIdentifier
token: map
element: map@83
staticType: Map<int, C>
leftBracket: [
index: IntegerLiteral
literal: 1
correspondingParameter: ParameterMember
baseElement: dart:core::<fragment>::@class::Map::@method::[]=::@parameter::key#element
substitution: {K: int, V: C}
staticType: int
rightBracket: ]
element: <null>
staticType: null
operator: =
rightHandSide: SimpleIdentifier
token: c
correspondingParameter: ParameterMember
baseElement: dart:core::<fragment>::@class::Map::@method::[]=::@parameter::value#element
substitution: {K: int, V: C}
element: <testLibraryFragment>::@function::foo::@parameter::c#element
staticType: C
readElement2: <null>
readType: null
writeElement2: MethodMember
baseElement: dart:core::@class::Map::@method::[]=
substitution: {K: int, V: C}
writeType: C
element: <null>
staticType: C
element: <testLibrary>::@class::C::@method::call
staticType: void Function(int)
''');
}
test_conditional_else() async {
await assertNoErrorsInCode('''
abstract class A {}
abstract class C extends A {
void call();
}
void Function() f(A a, bool b, C c, dynamic d) => b ? d : (b ? a : c);
''');
// `c` is in the "else" position of a conditional expression, so implicit
// call tearoff logic should not apply to it.
// Therefore the type of `b ? a : c` should be `A`.
var expr = findNode.conditionalExpression('b ? a : c');
assertResolvedNodeText(expr, r'''
ConditionalExpression
condition: SimpleIdentifier
token: b
element: <testLibraryFragment>::@function::f::@parameter::b#element
staticType: bool
question: ?
thenExpression: SimpleIdentifier
token: a
element: <testLibraryFragment>::@function::f::@parameter::a#element
staticType: A
colon: :
elseExpression: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
staticType: A
''');
}
test_conditional_then() async {
await assertNoErrorsInCode('''
abstract class A {}
abstract class C extends A {
void call();
}
void Function() f(A a, bool b, C c, dynamic d) => b ? d : (b ? c : a);
''');
// `c` is in the "then" position of a conditional expression, so implicit
// call tearoff logic should not apply to it.
// Therefore the type of `b ? c : a` should be `A`.
var expr = findNode.conditionalExpression('b ? c : a');
assertResolvedNodeText(expr, r'''
ConditionalExpression
condition: SimpleIdentifier
token: b
element: <testLibraryFragment>::@function::f::@parameter::b#element
staticType: bool
question: ?
thenExpression: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
colon: :
elseExpression: SimpleIdentifier
token: a
element: <testLibraryFragment>::@function::f::@parameter::a#element
staticType: A
staticType: A
''');
}
test_explicitTypeArguments() async {
await assertNoErrorsInCode('''
class C {
T call<T>(T t) => t;
}
void foo() {
var c = C();
c<int>;
}
''');
var node = findNode.implicitCallReference('c<int>');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: SimpleIdentifier
token: c
element: c@55
staticType: C
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: int
element2: dart:core::@class::int
type: int
rightBracket: >
element: <testLibrary>::@class::C::@method::call
staticType: int Function(int)
typeArgumentTypes
int
''');
}
test_ifNull_lhs() async {
await assertErrorsInCode(
'''
abstract class A {}
abstract class C extends A {
void call();
}
void Function() f(A a, bool b, C c, dynamic d) => b ? d : c ?? a;
''',
[
error(WarningCode.DEAD_CODE, 127, 4),
error(StaticWarningCode.DEAD_NULL_AWARE_EXPRESSION, 130, 1),
],
);
// `c` is on the LHS of an if-null expression, so implicit call tearoff
// logic should not apply to it.
// Therefore the type of `c ?? a` should be `A`.
var expr = findNode.binary('c ?? a');
assertResolvedNodeText(expr, r'''
BinaryExpression
leftOperand: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
operator: ??
rightOperand: SimpleIdentifier
token: a
correspondingParameter: <null>
element: <testLibraryFragment>::@function::f::@parameter::a#element
staticType: A
element: <null>
staticInvokeType: null
staticType: A
''');
}
test_ifNull_rhs() async {
await assertNoErrorsInCode('''
abstract class C {
void call(int t) => t;
}
void Function(int) foo(C? c1, C c2) {
return c1 ?? c2;
}
''');
var node = findNode.implicitCallReference('c1 ?? c2');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: BinaryExpression
leftOperand: SimpleIdentifier
token: c1
element: <testLibraryFragment>::@function::foo::@parameter::c1#element
staticType: C?
operator: ??
rightOperand: SimpleIdentifier
token: c2
correspondingParameter: <null>
element: <testLibraryFragment>::@function::foo::@parameter::c2#element
staticType: C
element: <null>
staticInvokeType: null
staticType: C
element: <testLibrary>::@class::C::@method::call
staticType: void Function(int)
''');
}
test_listLiteral_element() async {
await assertNoErrorsInCode('''
abstract class C {
void call(int t) => t;
}
List<void Function(int)> foo(C c) {
return [c];
}
''');
var node = findNode.implicitCallReference('c]');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::foo::@parameter::c#element
staticType: C
element: <testLibrary>::@class::C::@method::call
staticType: void Function(int)
''');
}
test_listLiteral_forElement() async {
await assertNoErrorsInCode('''
abstract class C {
void call(int t) => t;
}
List<void Function(int)> foo(C c) {
return [
for (var _ in [1, 2, 3]) c,
];
}
''');
var node = findNode.implicitCallReference('c,');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::foo::@parameter::c#element
staticType: C
element: <testLibrary>::@class::C::@method::call
staticType: void Function(int)
''');
}
test_listLiteral_ifElement() async {
await assertNoErrorsInCode('''
abstract class C {
void call(int t) => t;
}
List<void Function(int)> foo(C c) {
return [
if (1==2) c,
];
}
''');
var node = findNode.implicitCallReference('c,');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::foo::@parameter::c#element
staticType: C
element: <testLibrary>::@class::C::@method::call
staticType: void Function(int)
''');
}
test_listLiteral_ifElement_else() async {
await assertNoErrorsInCode('''
abstract class C {
void call(int t) => t;
}
List<void Function(int)> foo(C c1, C c2) {
return [
if (1==2) c1
else c2,
];
}
''');
var node = findNode.implicitCallReference('c2,');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: SimpleIdentifier
token: c2
element: <testLibraryFragment>::@function::foo::@parameter::c2#element
staticType: C
element: <testLibrary>::@class::C::@method::call
staticType: void Function(int)
''');
}
test_parenthesized_cascade_target() async {
await assertNoErrorsInCode('''
abstract class C {
void call();
void m();
}
void Function() f(C c) => (c)..m();
''');
var node = findNode.implicitCallReference('(c)');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: CascadeExpression
target: ParenthesizedExpression
leftParenthesis: (
expression: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::f::@parameter::c#element
staticType: C
rightParenthesis: )
staticType: C
cascadeSections
MethodInvocation
operator: ..
methodName: SimpleIdentifier
token: m
element: <testLibrary>::@class::C::@method::m
staticType: void Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticInvokeType: void Function()
staticType: void
staticType: C
element: <testLibrary>::@class::C::@method::call
staticType: void Function()
''');
}
test_prefixedIdentifier() async {
await assertNoErrorsInCode('''
abstract class C {
C get c;
void call(int t) => t;
}
void Function(int) foo(C c) {
return c.c;
}
''');
var node = findNode.implicitCallReference('c.c;');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: PrefixedIdentifier
prefix: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::foo::@parameter::c#element
staticType: C
period: .
identifier: SimpleIdentifier
token: c
element: <testLibraryFragment>::@class::C::@getter::c#element
staticType: C
element: <testLibraryFragment>::@class::C::@getter::c#element
staticType: C
element: <testLibrary>::@class::C::@method::call
staticType: void Function(int)
''');
}
test_propertyAccess() async {
await assertNoErrorsInCode('''
abstract class C {
C get c;
void call(int t) => t;
}
void Function(int) foo(C c) {
return c.c.c;
}
''');
var node = findNode.implicitCallReference('c.c.c');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: PropertyAccess
target: PrefixedIdentifier
prefix: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::foo::@parameter::c#element
staticType: C
period: .
identifier: SimpleIdentifier
token: c
element: <testLibraryFragment>::@class::C::@getter::c#element
staticType: C
element: <testLibraryFragment>::@class::C::@getter::c#element
staticType: C
operator: .
propertyName: SimpleIdentifier
token: c
element: <testLibraryFragment>::@class::C::@getter::c#element
staticType: C
staticType: C
element: <testLibrary>::@class::C::@method::call
staticType: void Function(int)
''');
}
test_setOrMapLiteral_element() async {
await assertNoErrorsInCode('''
abstract class C {
void call(int t) => t;
}
Set<void Function(int)> foo(C c) {
return {c};
}
''');
var node = findNode.implicitCallReference('c}');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::foo::@parameter::c#element
staticType: C
element: <testLibrary>::@class::C::@method::call
staticType: void Function(int)
''');
}
test_setOrMapLiteral_mapLiteralEntry_key() async {
await assertNoErrorsInCode('''
abstract class C {
void call(int t) => t;
}
Map<void Function(int), int> foo(C c) {
return {c: 1};
}
''');
var node = findNode.implicitCallReference('c:');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::foo::@parameter::c#element
staticType: C
element: <testLibrary>::@class::C::@method::call
staticType: void Function(int)
''');
}
test_setOrMapLiteral_mapLiteralEntry_value() async {
await assertNoErrorsInCode('''
abstract class C {
void call(int t) => t;
}
Map<int, void Function(int)> foo(C c) {
return {1: c};
}
''');
var node = findNode.implicitCallReference('c}');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::foo::@parameter::c#element
staticType: C
element: <testLibrary>::@class::C::@method::call
staticType: void Function(int)
''');
}
test_simpleIdentifier() async {
await assertNoErrorsInCode('''
abstract class C {
void call(int t) => t;
}
void Function(int) foo(C c) {
return c;
}
''');
var node = findNode.implicitCallReference('c;');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: SimpleIdentifier
token: c
element: <testLibraryFragment>::@function::foo::@parameter::c#element
staticType: C
element: <testLibrary>::@class::C::@method::call
staticType: void Function(int)
''');
}
test_simpleIdentifier_typeAlias() async {
await assertNoErrorsInCode('''
class A {
void call() {}
}
typedef B = A;
Function f(B b) => b;
''');
var node = findNode.implicitCallReference('b;');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: SimpleIdentifier
token: b
element: <testLibraryFragment>::@function::f::@parameter::b#element
staticType: A
alias: <testLibrary>::@typeAlias::B
element: <testLibrary>::@class::A::@method::call
staticType: void Function()
''');
}
test_simpleIdentifier_typeVariable() async {
await assertNoErrorsInCode('''
class A {
void call() {}
}
Function f<X extends A>(X x) => x;
''');
var node = findNode.implicitCallReference('x;');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: SimpleIdentifier
token: x
element: <testLibraryFragment>::@function::f::@parameter::x#element
staticType: X
element: <testLibrary>::@class::A::@method::call
staticType: void Function()
''');
}
test_simpleIdentifier_typeVariable2() async {
await assertNoErrorsInCode('''
class A {
void call() {}
}
Function f<X extends A, Y extends X>(Y y) => y;
''');
var node = findNode.implicitCallReference('y;');
assertResolvedNodeText(node, r'''
ImplicitCallReference
expression: SimpleIdentifier
token: y
element: <testLibraryFragment>::@function::f::@parameter::y#element
staticType: Y
element: <testLibrary>::@class::A::@method::call
staticType: void Function()
''');
}
test_simpleIdentifier_typeVariable2_nullable() async {
await assertErrorsInCode(
'''
class A {
void call() {}
}
Function f<X extends A, Y extends X?>(Y y) => y;
''',
[error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_FUNCTION, 75, 1)],
);
// Verify that no ImplicitCallReference was inserted.
var node = findNode.expressionFunctionBody('y;').expression;
assertResolvedNodeText(node, r'''
SimpleIdentifier
token: y
element: <testLibraryFragment>::@function::f::@parameter::y#element
staticType: Y
''');
}
test_simpleIdentifier_typeVariable_nullable() async {
await assertErrorsInCode(
'''
class A {
void call() {}
}
Function f<X extends A>(X? x) => x;
''',
[error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_FUNCTION, 62, 1)],
);
// Verify that no ImplicitCallReference was inserted.
var node = findNode.expressionFunctionBody('x;').expression;
assertResolvedNodeText(node, r'''
SimpleIdentifier
token: x
element: <testLibraryFragment>::@function::f::@parameter::x#element
staticType: X?
''');
}
}
@reflectiveTest
class AstRewriteMethodInvocationTest extends PubPackageResolutionTest
with AstRewriteMethodInvocationTestCases {}
mixin AstRewriteMethodInvocationTestCases on PubPackageResolutionTest {
test_targetNull_cascade() async {
await assertNoErrorsInCode(r'''
class A {
void foo() {}
}
f(A a) {
a..foo();
}
''');
var node = findNode.methodInvocation('foo();');
assertResolvedNodeText(node, r'''
MethodInvocation
operator: ..
methodName: SimpleIdentifier
token: foo
element: <testLibrary>::@class::A::@method::foo
staticType: void Function()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticInvokeType: void Function()
staticType: void
''');
}
test_targetNull_class() async {
await assertNoErrorsInCode(r'''
class A<T, U> {
A(int a);
}
f() {
A<int, String>(0);
}
''');
var node = findNode.instanceCreation('A<int, String>(0);');
assertResolvedNodeText(node, r'''
InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: A
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: int
element2: dart:core::@class::int
type: int
NamedType
name: String
element2: dart:core::@class::String
type: String
rightBracket: >
element2: <testLibrary>::@class::A
type: A<int, String>
element: ConstructorMember
baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
substitution: {T: int, U: String}
argumentList: ArgumentList
leftParenthesis: (
arguments
IntegerLiteral
literal: 0
correspondingParameter: ParameterMember
baseElement: <testLibraryFragment>::@class::A::@constructor::new::@parameter::a#element
substitution: {T: int, U: String}
staticType: int
rightParenthesis: )
staticType: A<int, String>
''');
}
test_targetNull_extension() async {
await assertNoErrorsInCode(r'''
class A {}
extension E<T> on A {
void foo() {}
}
f(A a) {
E<int>(a).foo();
}
''');
var node = findNode.extensionOverride('E<int>(a)');
assertResolvedNodeText(node, r'''
ExtensionOverride
name: E
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: int
element2: dart:core::@class::int
type: int
rightBracket: >
argumentList: ArgumentList
leftParenthesis: (
arguments
SimpleIdentifier
token: a
correspondingParameter: <null>
element: <testLibraryFragment>::@function::f::@parameter::a#element
staticType: A
rightParenthesis: )
element2: <testLibrary>::@extension::E
extendedType: A
staticType: null
typeArgumentTypes
int
''');
}
test_targetNull_function() async {
await assertNoErrorsInCode(r'''
void A<T, U>(int a) {}
f() {
A<int, String>(0);
}
''');
var node = findNode.methodInvocation('A<int, String>(0);');
assertResolvedNodeText(node, r'''
MethodInvocation
methodName: SimpleIdentifier
token: A
element: <testLibrary>::@function::A
staticType: void Function<T, U>(int)
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: int
element2: dart:core::@class::int
type: int
NamedType
name: String
element2: dart:core::@class::String
type: String
rightBracket: >
argumentList: ArgumentList
leftParenthesis: (
arguments
IntegerLiteral
literal: 0
correspondingParameter: ParameterMember
baseElement: <testLibraryFragment>::@function::A::@parameter::a#element
substitution: {T: int, U: String}
staticType: int
rightParenthesis: )
staticInvokeType: void Function(int)
staticType: void
typeArgumentTypes
int
String
''');
}
test_targetNull_typeAlias_interfaceType() async {
await assertNoErrorsInCode(r'''
class A<T, U> {
A(int _);
}
typedef X<T, U> = A<T, U>;
void f() {
X<int, String>(0);
}
''');
var node = findNode.instanceCreation('X<int, String>(0);');
assertResolvedNodeText(node, r'''
InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: X
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: int
element2: dart:core::@class::int
type: int
NamedType
name: String
element2: dart:core::@class::String
type: String
rightBracket: >
element2: <testLibrary>::@typeAlias::X
type: A<int, String>
element: ConstructorMember
baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
substitution: {T: int, U: String}
argumentList: ArgumentList
leftParenthesis: (
arguments
IntegerLiteral
literal: 0
correspondingParameter: ParameterMember
baseElement: <testLibraryFragment>::@class::A::@constructor::new::@parameter::_#element
substitution: {T: int, U: String}
staticType: int
rightParenthesis: )
staticType: A<int, String>
''');
}
test_targetNull_typeAlias_Never() async {
await assertErrorsInCode(
r'''
typedef X = Never;
void f() {
X(0);
}
''',
[error(CompileTimeErrorCode.INVOCATION_OF_NON_FUNCTION, 33, 1)],
);
// Not rewritten.
findNode.methodInvocation('X(0)');
}
test_targetPrefixedIdentifier_prefix_class_constructor() async {
newFile('$testPackageLibPath/a.dart', r'''
class A<T> {
A.named(T a);
}
''');
await assertNoErrorsInCode(r'''
import 'a.dart' as prefix;
f() {
prefix.A.named(0);
}
''');
var node = findNode.instanceCreation('A.named(0);');
assertResolvedNodeText(node, r'''
InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
importPrefix: ImportPrefixReference
name: prefix
period: .
element2: <testLibraryFragment>::@prefix2::prefix
name: A
element2: package:test/a.dart::@class::A
type: A<int>
period: .
name: SimpleIdentifier
token: named
element: ConstructorMember
baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::named#element
substitution: {T: dynamic}
staticType: null
element: ConstructorMember
baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::named#element
substitution: {T: int}
argumentList: ArgumentList
leftParenthesis: (
arguments
IntegerLiteral
literal: 0
correspondingParameter: ParameterMember
baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::named::@parameter::a#element
substitution: {T: int}
staticType: int
rightParenthesis: )
staticType: A<int>
''');
}
test_targetPrefixedIdentifier_prefix_class_constructor_typeArguments() async {
newFile('$testPackageLibPath/a.dart', r'''
class A<T> {
A.named(int a);
}
''');
await assertErrorsInCode(
r'''
import 'a.dart' as prefix;
f() {
prefix.A.named<int>(0);
}
''',
[
error(
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR,
50,
5,
messageContains: ["The constructor 'prefix.A.named'"],
),
],
);
var node = findNode.instanceCreation('named<int>(0);');
assertResolvedNodeText(node, r'''
InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
importPrefix: ImportPrefixReference
name: prefix
period: .
element2: <testLibraryFragment>::@prefix2::prefix
name: A
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: int
element2: dart:core::@class::int
type: int
rightBracket: >
element2: package:test/a.dart::@class::A
type: A<int>
period: .
name: SimpleIdentifier
token: named
element: ConstructorMember
baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::named#element
substitution: {T: int}
staticType: null
element: ConstructorMember
baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::named#element
substitution: {T: int}
argumentList: ArgumentList
leftParenthesis: (
arguments
IntegerLiteral
literal: 0
correspondingParameter: ParameterMember
baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::named::@parameter::a#element
substitution: {T: int}
staticType: int
rightParenthesis: )
staticType: A<int>
''');
}
test_targetPrefixedIdentifier_prefix_class_constructor_typeArguments_new() async {
newFile('$testPackageLibPath/a.dart', r'''
class A<T> {
A.new(int a);
}
''');
await assertErrorsInCode(
r'''
import 'a.dart' as prefix;
f() {
prefix.A.new<int>(0);
}
''',
[
error(
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR,
48,
5,
messageContains: ["The constructor 'prefix.A.new'"],
),
],
);
var node = findNode.instanceCreation('new<int>(0);');
assertResolvedNodeText(node, r'''
InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
importPrefix: ImportPrefixReference
name: prefix
period: .
element2: <testLibraryFragment>::@prefix2::prefix
name: A
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: int
element2: dart:core::@class::int
type: int
rightBracket: >
element2: package:test/a.dart::@class::A
type: A<int>
period: .
name: SimpleIdentifier
token: new
element: ConstructorMember
baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::new#element
substitution: {T: int}
staticType: null
element: ConstructorMember
baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::new#element
substitution: {T: int}
argumentList: ArgumentList
leftParenthesis: (
arguments
IntegerLiteral
literal: 0
correspondingParameter: ParameterMember
baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::new::@parameter::a#element
substitution: {T: int}
staticType: int
rightParenthesis: )
staticType: A<int>
''');
}
test_targetPrefixedIdentifier_prefix_getter_method() async {
newFile('$testPackageLibPath/a.dart', r'''
A get foo => A();
class A {
void bar(int a) {}
}
''');
await assertNoErrorsInCode(r'''
import 'a.dart' as prefix;
f() {
prefix.foo.bar(0);
}
''');
var node = findNode.methodInvocation('bar(0);');
assertResolvedNodeText(node, r'''
MethodInvocation
target: PrefixedIdentifier
prefix: SimpleIdentifier
token: prefix
element: <testLibraryFragment>::@prefix2::prefix
staticType: null
period: .
identifier: SimpleIdentifier
token: foo
element: package:test/a.dart::<fragment>::@getter::foo#element
staticType: A
element: package:test/a.dart::<fragment>::@getter::foo#element
staticType: A
operator: .
methodName: SimpleIdentifier
token: bar
element: package:test/a.dart::@class::A::@method::bar
staticType: void Function(int)
argumentList: ArgumentList
leftParenthesis: (
arguments
IntegerLiteral
literal: 0
correspondingParameter: package:test/a.dart::<fragment>::@class::A::@method::bar::@parameter::a#element
staticType: int
rightParenthesis: )
staticInvokeType: void Function(int)
staticType: void
''');
}
test_targetPrefixedIdentifier_typeAlias_interfaceType_constructor() async {
newFile('$testPackageLibPath/a.dart', r'''
class A<T> {
A.named(T a);
}
typedef X<T> = A<T>;
''');
await assertNoErrorsInCode(r'''
import 'a.dart' as prefix;
void f() {
prefix.X.named(0);
}
''');
var node = findNode.instanceCreation('X.named(0);');
assertResolvedNodeText(node, r'''
InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
importPrefix: ImportPrefixReference
name: prefix
period: .
element2: <testLibraryFragment>::@prefix2::prefix
name: X
element2: package:test/a.dart::@typeAlias::X
type: A<int>
period: .
name: SimpleIdentifier
token: named
element: ConstructorMember
baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::named#element
substitution: {T: dynamic}
staticType: null
element: ConstructorMember
baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::named#element
substitution: {T: int}
argumentList: ArgumentList
leftParenthesis: (
arguments
IntegerLiteral
literal: 0
correspondingParameter: ParameterMember
baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::named::@parameter::a#element
substitution: {T: int}
staticType: int
rightParenthesis: )
staticType: A<int>
''');
}
test_targetSimpleIdentifier_class_constructor() async {
await assertNoErrorsInCode(r'''
class A<T> {
A.named(T a);
}
f() {
A.named(0);
}
''');
var node = findNode.instanceCreation('A.named(0);');
assertResolvedNodeText(node, r'''
InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: A
element2: <testLibrary>::@class::A
type: A<int>
period: .
name: SimpleIdentifier
token: named
element: ConstructorMember
baseElement: <testLibraryFragment>::@class::A::@constructor::named#element
substitution: {T: dynamic}
staticType: null
element: ConstructorMember
baseElement: <testLibraryFragment>::@class::A::@constructor::named#element
substitution: {T: int}
argumentList: ArgumentList
leftParenthesis: (
arguments
IntegerLiteral
literal: 0
correspondingParameter: ParameterMember
baseElement: <testLibraryFragment>::@class::A::@constructor::named::@parameter::a#element
substitution: {T: int}
staticType: int
rightParenthesis: )
staticType: A<int>
''');
}
test_targetSimpleIdentifier_class_constructor_typeArguments() async {
await assertErrorsInCode(
r'''
class A<T, U> {
A.named(int a);
}
f() {
A.named<int, String>(0);
}
''',
[
error(
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR,
52,
13,
messageContains: ["The constructor 'A.named'"],
),
],
);
// TODO(scheglov): Move type arguments
var node = findNode.instanceCreation('named<int, String>(0);');
assertResolvedNodeText(node, r'''
InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: A
element2: <testLibrary>::@class::A
type: A<dynamic, dynamic>
period: .
name: SimpleIdentifier
token: named
element: ConstructorMember
baseElement: <testLibraryFragment>::@class::A::@constructor::named#element
substitution: {T: dynamic, U: dynamic}
staticType: null
element: ConstructorMember
baseElement: <testLibraryFragment>::@class::A::@constructor::named#element
substitution: {T: dynamic, U: dynamic}
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: int
element2: dart:core::@class::int
type: int
NamedType
name: String
element2: dart:core::@class::String
type: String
rightBracket: >
argumentList: ArgumentList
leftParenthesis: (
arguments
IntegerLiteral
literal: 0
correspondingParameter: ParameterMember
baseElement: <testLibraryFragment>::@class::A::@constructor::named::@parameter::a#element
substitution: {T: dynamic, U: dynamic}
staticType: int
rightParenthesis: )
staticType: A<dynamic, dynamic>
''');
}
test_targetSimpleIdentifier_class_constructor_typeArguments_new() async {
await assertErrorsInCode(
r'''
class A<T, U> {
A.new(int a);
}
f() {
A.new<int, String>(0);
}
''',
[
error(
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR,
48,
13,
messageContains: ["The constructor 'A.new'"],
),
],
);
// TODO(scheglov): Move type arguments
var node = findNode.instanceCreation('new<int, String>(0);');
assertResolvedNodeText(node, r'''
InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: A
element2: <testLibrary>::@class::A
type: A<dynamic, dynamic>
period: .
name: SimpleIdentifier
token: new
element: ConstructorMember
baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
substitution: {T: dynamic, U: dynamic}
staticType: null
element: ConstructorMember
baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
substitution: {T: dynamic, U: dynamic}
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: int
element2: dart:core::@class::int
type: int
NamedType
name: String
element2: dart:core::@class::String
type: String
rightBracket: >
argumentList: ArgumentList
leftParenthesis: (
arguments
IntegerLiteral
literal: 0
correspondingParameter: ParameterMember
baseElement: <testLibraryFragment>::@class::A::@constructor::new::@parameter::a#element
substitution: {T: dynamic, U: dynamic}
staticType: int
rightParenthesis: )
staticType: A<dynamic, dynamic>
''');
}
test_targetSimpleIdentifier_class_staticMethod() async {
await assertNoErrorsInCode(r'''
class A {
static void foo(int a) {}
}
f() {
A.foo(0);
}
''');
var node = findNode.methodInvocation('foo(0);');
assertResolvedNodeText(node, r'''
MethodInvocation
target: SimpleIdentifier
token: A
element: <testLibrary>::@class::A
staticType: null
operator: .
methodName: SimpleIdentifier
token: foo
element: <testLibrary>::@class::A::@method::foo
staticType: void Function(int)
argumentList: ArgumentList
leftParenthesis: (
arguments
IntegerLiteral
literal: 0
correspondingParameter: <testLibraryFragment>::@class::A::@method::foo::@parameter::a#element
staticType: int
rightParenthesis: )
staticInvokeType: void Function(int)
staticType: void
''');
}
test_targetSimpleIdentifier_prefix_class() async {
newFile('$testPackageLibPath/a.dart', r'''
class A<T, U> {
A(int a);
}
''');
await assertNoErrorsInCode(r'''
import 'a.dart' as prefix;
f() {
prefix.A<int, String>(0);
}
''');
var node = findNode.instanceCreation('A<int, String>(0);');
assertResolvedNodeText(node, r'''
InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
importPrefix: ImportPrefixReference
name: prefix
period: .
element2: <testLibraryFragment>::@prefix2::prefix
name: A
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: int
element2: dart:core::@class::int
type: int
NamedType
name: String
element2: dart:core::@class::String
type: String
rightBracket: >
element2: package:test/a.dart::@class::A
type: A<int, String>
element: ConstructorMember
baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::new#element
substitution: {T: int, U: String}
argumentList: ArgumentList
leftParenthesis: (
arguments
IntegerLiteral
literal: 0
correspondingParameter: ParameterMember
baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::new::@parameter::a#element
substitution: {T: int, U: String}
staticType: int
rightParenthesis: )
staticType: A<int, String>
''');
}
test_targetSimpleIdentifier_prefix_extension() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {}
extension E<T> on A {
void foo() {}
}
''');
await assertNoErrorsInCode(r'''
import 'a.dart' as prefix;
f(prefix.A a) {
prefix.E<int>(a).foo();
}
''');
var node = findNode.extensionOverride('E<int>(a)');
assertResolvedNodeText(node, r'''
ExtensionOverride
importPrefix: ImportPrefixReference
name: prefix
period: .
element2: <testLibraryFragment>::@prefix2::prefix
name: E
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: int
element2: dart:core::@class::int
type: int
rightBracket: >
argumentList: ArgumentList
leftParenthesis: (
arguments
SimpleIdentifier
token: a
correspondingParameter: <null>
element: <testLibraryFragment>::@function::f::@parameter::a#element
staticType: A
rightParenthesis: )
element2: package:test/a.dart::@extension::E
extendedType: A
staticType: null
typeArgumentTypes
int
''');
}
test_targetSimpleIdentifier_prefix_function() async {
newFile('$testPackageLibPath/a.dart', r'''
void A<T, U>(int a) {}
''');
await assertNoErrorsInCode(r'''
import 'a.dart' as prefix;
f() {
prefix.A<int, String>(0);
}
''');
var node = findNode.methodInvocation('A<int, String>(0);');
assertResolvedNodeText(node, r'''
MethodInvocation
target: SimpleIdentifier
token: prefix
element: <testLibraryFragment>::@prefix2::prefix
staticType: null
operator: .
methodName: SimpleIdentifier
token: A
element: package:test/a.dart::@function::A
staticType: void Function<T, U>(int)
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: int
element2: dart:core::@class::int
type: int
NamedType
name: String
element2: dart:core::@class::String
type: String
rightBracket: >
argumentList: ArgumentList
leftParenthesis: (
arguments
IntegerLiteral
literal: 0
correspondingParameter: ParameterMember
baseElement: package:test/a.dart::<fragment>::@function::A::@parameter::a#element
substitution: {T: int, U: String}
staticType: int
rightParenthesis: )
staticInvokeType: void Function(int)
staticType: void
typeArgumentTypes
int
String
''');
}
test_targetSimpleIdentifier_typeAlias_interfaceType_constructor() async {
await assertNoErrorsInCode(r'''
class A<T> {
A.named(T a);
}
typedef X<T> = A<T>;
void f() {
X.named(0);
}
''');
var node = findNode.instanceCreation('X.named(0);');
assertResolvedNodeText(node, r'''
InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: X
element2: <testLibrary>::@typeAlias::X
type: A<int>
period: .
name: SimpleIdentifier
token: named
element: ConstructorMember
baseElement: <testLibraryFragment>::@class::A::@constructor::named#element
substitution: {T: dynamic}
staticType: null
element: ConstructorMember
baseElement: <testLibraryFragment>::@class::A::@constructor::named#element
substitution: {T: int}
argumentList: ArgumentList
leftParenthesis: (
arguments
IntegerLiteral
literal: 0
correspondingParameter: ParameterMember
baseElement: <testLibraryFragment>::@class::A::@constructor::named::@parameter::a#element
substitution: {T: int}
staticType: int
rightParenthesis: )
staticType: A<int>
''');
}
}
@reflectiveTest
class AstRewritePrefixedIdentifierTest extends PubPackageResolutionTest {
test_constructorReference_inAssignment_onLeftSide() async {
await assertErrorsInCode(
'''
class C {}
void f() {
C.new = 1;
}
''',
[error(CompileTimeErrorCode.UNDEFINED_SETTER, 27, 3)],
);
var identifier = findNode.prefixed('C.new');
// The left side of the assignment is resolved by
// [PropertyElementResolver._resolveTargetClassElement], which looks for
// getters and setters on `C`, and does not recover with other elements
// (methods, constructors). This prefixed identifier can have a real
// `staticElement` if we add such recovery.
expect(identifier.element, isNull);
}
test_constructorReference_inAssignment_onRightSide() async {
await assertNoErrorsInCode('''
class C {}
Function? f;
void g() {
f = C.new;
}
''');
var node = findNode.constructorReference('C.new');
assertResolvedNodeText(node, r'''
ConstructorReference
constructorName: ConstructorName
type: NamedType
name: C
element2: <testLibrary>::@class::C
type: null
period: .
name: SimpleIdentifier
token: new
element: <testLibraryFragment>::@class::C::@constructor::new#element
staticType: null
element: <testLibraryFragment>::@class::C::@constructor::new#element
correspondingParameter: <testLibraryFragment>::@setter::f::@parameter::_f#element
staticType: C Function()
''');
}
// TODO(srawlins): Complete tests of all cases of rewriting (or not) a
// prefixed identifier.
}