blob: a84ddc6cf94e66c3fa51198824a005ac62407da9 [file] [log] [blame]
// Copyright (c) 2016, 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 'dart:async';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/standard_resolution_map.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/source_io.dart';
import 'package:analyzer/src/task/strong/ast_properties.dart';
import 'package:front_end/src/base/errors.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../utils.dart';
import 'resolver_test_case.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(StrongModeLocalInferenceTest);
defineReflectiveTests(StrongModeStaticTypeAnalyzer2Test);
defineReflectiveTests(StrongModeTypePropagationTest);
});
}
/**
* Strong mode static analyzer local type inference tests
*/
@reflectiveTest
class StrongModeLocalInferenceTest extends ResolverTestCase {
TypeAssertions _assertions;
Asserter<DartType> _isDynamic;
Asserter<InterfaceType> _isFutureOfDynamic;
Asserter<InterfaceType> _isFutureOfInt;
Asserter<InterfaceType> _isFutureOfNull;
Asserter<InterfaceType> _isFutureOrOfInt;
Asserter<DartType> _isInt;
Asserter<DartType> _isNull;
Asserter<DartType> _isNum;
Asserter<DartType> _isObject;
Asserter<DartType> _isString;
AsserterBuilder2<Asserter<DartType>, Asserter<DartType>, DartType>
_isFunction2Of;
AsserterBuilder<List<Asserter<DartType>>, InterfaceType> _isFutureOf;
AsserterBuilder<List<Asserter<DartType>>, InterfaceType> _isFutureOrOf;
AsserterBuilderBuilder<Asserter<DartType>, List<Asserter<DartType>>, DartType>
_isInstantiationOf;
AsserterBuilder<Asserter<DartType>, InterfaceType> _isListOf;
AsserterBuilder2<Asserter<DartType>, Asserter<DartType>, InterfaceType>
_isMapOf;
AsserterBuilder<List<Asserter<DartType>>, InterfaceType> _isStreamOf;
AsserterBuilder<DartType, DartType> _isType;
AsserterBuilder<Element, DartType> _hasElement;
AsserterBuilder<DartType, DartType> _hasElementOf;
@override
Future<TestAnalysisResult> computeAnalysisResult(Source source) async {
TestAnalysisResult result = await super.computeAnalysisResult(source);
if (_assertions == null) {
_assertions = new TypeAssertions(typeProvider);
_isType = _assertions.isType;
_hasElement = _assertions.hasElement;
_isInstantiationOf = _assertions.isInstantiationOf;
_isInt = _assertions.isInt;
_isNull = _assertions.isNull;
_isNum = _assertions.isNum;
_isObject = _assertions.isObject;
_isString = _assertions.isString;
_isDynamic = _assertions.isDynamic;
_isListOf = _assertions.isListOf;
_isMapOf = _assertions.isMapOf;
_isFunction2Of = _assertions.isFunction2Of;
_hasElementOf = _assertions.hasElementOf;
_isFutureOf = _isInstantiationOf(_hasElementOf(typeProvider.futureType));
_isFutureOrOf =
_isInstantiationOf(_hasElementOf(typeProvider.futureOrType));
_isFutureOfDynamic = _isFutureOf([_isDynamic]);
_isFutureOfInt = _isFutureOf([_isInt]);
_isFutureOfNull = _isFutureOf([_isNull]);
_isFutureOrOfInt = _isFutureOrOf([_isInt]);
_isStreamOf = _isInstantiationOf(_hasElementOf(typeProvider.streamType));
}
return result;
}
@override
void setUp() {
super.setUp();
AnalysisOptionsImpl options = new AnalysisOptionsImpl();
options.strongMode = true;
resetWith(options: options);
}
test_async_method_propagation() async {
String code = r'''
import "dart:async";
class A {
Future f0() => new Future.value(3);
Future f1() async => new Future.value(3);
Future f2() async => await new Future.value(3);
Future<int> f3() => new Future.value(3);
Future<int> f4() async => new Future.value(3);
Future<int> f5() async => await new Future.value(3);
Future g0() { return new Future.value(3); }
Future g1() async { return new Future.value(3); }
Future g2() async { return await new Future.value(3); }
Future<int> g3() { return new Future.value(3); }
Future<int> g4() async { return new Future.value(3); }
Future<int> g5() async { return await new Future.value(3); }
}
''';
CompilationUnit unit = await resolveSource(code);
void check(String name, Asserter<InterfaceType> typeTest) {
MethodDeclaration test = AstFinder.getMethodInClass(unit, "A", name);
FunctionBody body = test.body;
Expression returnExp;
if (body is ExpressionFunctionBody) {
returnExp = body.expression;
} else {
ReturnStatement stmt = (body as BlockFunctionBody).block.statements[0];
returnExp = stmt.expression;
}
DartType type = returnExp.staticType;
if (returnExp is AwaitExpression) {
type = returnExp.expression.staticType;
}
typeTest(type);
}
check("f0", _isFutureOfDynamic);
check("f1", _isFutureOfDynamic);
check("f2", _isFutureOfDynamic);
check("f3", _isFutureOfInt);
check("f4", _isFutureOfInt);
check("f5", _isFutureOfInt);
check("g0", _isFutureOfDynamic);
check("g1", _isFutureOfDynamic);
check("g2", _isFutureOfDynamic);
check("g3", _isFutureOfInt);
check("g4", _isFutureOfInt);
check("g5", _isFutureOfInt);
}
test_async_propagation() async {
String code = r'''
import "dart:async";
Future f0() => new Future.value(3);
Future f1() async => new Future.value(3);
Future f2() async => await new Future.value(3);
Future<int> f3() => new Future.value(3);
Future<int> f4() async => new Future.value(3);
Future<int> f5() async => await new Future.value(3);
Future g0() { return new Future.value(3); }
Future g1() async { return new Future.value(3); }
Future g2() async { return await new Future.value(3); }
Future<int> g3() { return new Future.value(3); }
Future<int> g4() async { return new Future.value(3); }
Future<int> g5() async { return await new Future.value(3); }
''';
CompilationUnit unit = await resolveSource(code);
void check(String name, Asserter<InterfaceType> typeTest) {
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, name);
FunctionBody body = test.functionExpression.body;
Expression returnExp;
if (body is ExpressionFunctionBody) {
returnExp = body.expression;
} else {
ReturnStatement stmt = (body as BlockFunctionBody).block.statements[0];
returnExp = stmt.expression;
}
DartType type = returnExp.staticType;
if (returnExp is AwaitExpression) {
type = returnExp.expression.staticType;
}
typeTest(type);
}
check("f0", _isFutureOfDynamic);
check("f1", _isFutureOfDynamic);
check("f2", _isFutureOfDynamic);
check("f3", _isFutureOfInt);
check("f4", _isFutureOfInt);
check("f5", _isFutureOfInt);
check("g0", _isFutureOfDynamic);
check("g1", _isFutureOfDynamic);
check("g2", _isFutureOfDynamic);
check("g3", _isFutureOfInt);
check("g4", _isFutureOfInt);
check("g5", _isFutureOfInt);
}
test_async_star_method_propagation() async {
String code = r'''
import "dart:async";
class A {
Stream g0() async* { yield []; }
Stream g1() async* { yield* new Stream(); }
Stream<List<int>> g2() async* { yield []; }
Stream<List<int>> g3() async* { yield* new Stream(); }
}
''';
CompilationUnit unit = await resolveSource(code);
void check(String name, Asserter<InterfaceType> typeTest) {
MethodDeclaration test = AstFinder.getMethodInClass(unit, "A", name);
BlockFunctionBody body = test.body;
YieldStatement stmt = body.block.statements[0];
Expression exp = stmt.expression;
typeTest(exp.staticType);
}
check("g0", _isListOf(_isDynamic));
check("g1", _isStreamOf([_isDynamic]));
check("g2", _isListOf(_isInt));
check("g3", _isStreamOf([(DartType type) => _isListOf(_isInt)(type)]));
}
test_async_star_propagation() async {
String code = r'''
import "dart:async";
Stream g0() async* { yield []; }
Stream g1() async* { yield* new Stream(); }
Stream<List<int>> g2() async* { yield []; }
Stream<List<int>> g3() async* { yield* new Stream(); }
''';
CompilationUnit unit = await resolveSource(code);
void check(String name, Asserter<InterfaceType> typeTest) {
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, name);
BlockFunctionBody body = test.functionExpression.body;
YieldStatement stmt = body.block.statements[0];
Expression exp = stmt.expression;
typeTest(exp.staticType);
}
check("g0", _isListOf(_isDynamic));
check("g1", _isStreamOf([_isDynamic]));
check("g2", _isListOf(_isInt));
check("g3", _isStreamOf([(DartType type) => _isListOf(_isInt)(type)]));
}
test_cascadeExpression() async {
String code = r'''
class A<T> {
List<T> map(T a, List<T> mapper(T x)) => mapper(a);
}
void main () {
A<int> a = new A()..map(0, (x) => [x]);
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
CascadeExpression fetch(int i) {
VariableDeclarationStatement stmt = statements[i];
VariableDeclaration decl = stmt.variables.variables[0];
CascadeExpression exp = decl.initializer;
return exp;
}
Element elementA = AstFinder.getClass(unit, "A").element;
CascadeExpression cascade = fetch(0);
_isInstantiationOf(_hasElement(elementA))([_isInt])(cascade.staticType);
MethodInvocation invoke = cascade.cascadeSections[0];
FunctionExpression function = invoke.argumentList.arguments[1];
ExecutableElement f0 = function.element;
_isListOf(_isInt)(f0.type.returnType);
expect(f0.type.normalParameterTypes[0], typeProvider.intType);
}
test_constrainedByBounds1() async {
// Test that upwards inference with two type variables correctly
// propogates from the constrained variable to the unconstrained
// variable if they are ordered left to right.
String code = r'''
T f<S, T extends S>(S x) => null;
void test() { var x = f(3); }
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test");
VariableDeclarationStatement stmt = statements[0];
VariableDeclaration decl = stmt.variables.variables[0];
Expression call = decl.initializer;
_isInt(call.staticType);
}
test_constrainedByBounds2() async {
// Test that upwards inference with two type variables does
// propogate from the constrained variable to the unconstrained
// variable if they are ordered right to left.
String code = r'''
T f<T extends S, S>(S x) => null;
void test() { var x = f(3); }
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test");
VariableDeclarationStatement stmt = statements[0];
VariableDeclaration decl = stmt.variables.variables[0];
Expression call = decl.initializer;
_isInt(call.staticType);
}
test_constrainedByBounds3() async {
Source source = addSource(r'''
T f<T extends S, S extends int>(S x) => null;
void test() { var x = f(3); }
''');
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test");
VariableDeclarationStatement stmt = statements[0];
VariableDeclaration decl = stmt.variables.variables[0];
Expression call = decl.initializer;
_isInt(call.staticType);
}
test_constrainedByBounds4() async {
// Test that upwards inference with two type variables correctly
// propogates from the constrained variable to the unconstrained
// variable if they are ordered left to right, when the variable
// appears co and contra variantly
String code = r'''
typedef To Func1<From, To>(From x);
T f<S, T extends Func1<S, S>>(S x) => null;
void test() { var x = f(3)(4); }
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test");
VariableDeclarationStatement stmt = statements[0];
VariableDeclaration decl = stmt.variables.variables[0];
Expression call = decl.initializer;
_isInt(call.staticType);
}
test_constrainedByBounds5() async {
// Test that upwards inference with two type variables does not
// propogate from the constrained variable to the unconstrained
// variable if they are ordered right to left, when the variable
// appears co and contra variantly, and that an error is issued
// for the non-matching bound.
String code = r'''
typedef To Func1<From, To>(From x);
T f<T extends Func1<S, S>, S>(S x) => null;
void test() { var x = f(3)(null); }
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertErrors(source, [StrongModeCode.COULD_NOT_INFER]);
verify([source]);
CompilationUnit unit = analysisResult.unit;
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test");
VariableDeclarationStatement stmt = statements[0];
VariableDeclaration decl = stmt.variables.variables[0];
Expression call = decl.initializer;
_isDynamic(call.staticType);
}
test_constructorInitializer_propagation() async {
String code = r'''
class A {
List<String> x;
A() : this.x = [];
}
''';
CompilationUnit unit = await resolveSource(code);
ConstructorDeclaration constructor =
AstFinder.getConstructorInClass(unit, "A", null);
ConstructorFieldInitializer assignment = constructor.initializers[0];
Expression exp = assignment.expression;
_isListOf(_isString)(exp.staticType);
}
test_covarianceChecks() async {
var source = addSource(r'''
class C<T> {
add(T t) {}
forEach(void f(T t)) {}
}
class D extends C<int> {
add(int t) {}
forEach(void f(int t)) {}
}
class E extends C<int> {
add(Object t) {}
forEach(void f(Null t)) {}
}
''');
var unit = (await computeAnalysisResult(source)).unit;
assertNoErrors(source);
var cAdd = AstFinder.getMethodInClass(unit, "C", "add");
var covariantC = getClassCovariantParameters(AstFinder.getClass(unit, "C"));
expect(covariantC.toList(), [cAdd.element.parameters[0]]);
var dAdd = AstFinder.getMethodInClass(unit, "D", "add");
var covariantD = getClassCovariantParameters(AstFinder.getClass(unit, "D"));
expect(covariantD.toList(), [dAdd.element.parameters[0]]);
var covariantE = getClassCovariantParameters(AstFinder.getClass(unit, "E"));
expect(covariantE.toList(), []);
}
test_covarianceChecks_genericMethods() async {
var source = addSource(r'''
class C<T> {
add<S>(T t) {}
forEach<S>(S f(T t)) {}
}
class D extends C<int> {
add<S>(int t) {}
forEach<S>(S f(int t)) {}
}
class E extends C<int> {
add<S>(Object t) {}
forEach<S>(S f(Null t)) {}
}
''');
var unit = (await computeAnalysisResult(source)).unit;
assertNoErrors(source);
var cAdd = AstFinder.getMethodInClass(unit, "C", "add");
var covariantC = getClassCovariantParameters(AstFinder.getClass(unit, "C"));
expect(covariantC.toList(), [cAdd.element.parameters[0]]);
var dAdd = AstFinder.getMethodInClass(unit, "D", "add");
var covariantD = getClassCovariantParameters(AstFinder.getClass(unit, "D"));
expect(covariantD.toList(), [dAdd.element.parameters[0]]);
var covariantE = getClassCovariantParameters(AstFinder.getClass(unit, "E"));
expect(covariantE.toList(), []);
}
test_covarianceChecks_returnFunction() async {
var source = addSource(r'''
typedef F<T>(T t);
typedef T R<T>();
class C<T> {
F<T> f;
C();
factory C.fact() => new C<Null>();
F<T> get g => null;
F<T> m1() => null;
R<F<T>> m2() => null;
casts(C<T> other, T t) {
other.f;
other.g(t);
other.m1();
other.m2;
new C<T>.fact().f(t);
new C<int>.fact().g;
new C<int>.fact().m1;
new C<T>.fact().m2();
new C<Object>.fact().f(42);
new C<Object>.fact().g;
new C<Object>.fact().m1;
new C<Object>.fact().m2();
new C.fact().f(42);
new C.fact().g;
new C.fact().m1;
new C.fact().m2();
}
noCasts(T t) {
f;
g;
m1();
m2();
f(t);
g(t);
(f)(t);
(g)(t);
m1;
m2;
this.f;
this.g;
this.m1();
this.m2();
this.m1;
this.m2;
(this.m1)();
(this.m2)();
this.f(t);
this.g(t);
(this.f)(t);
(this.g)(t);
new C<int>().f;
new C<T>().g;
new C<int>().m1();
new C().m2();
new D().f;
new D().g;
new D().m1();
new D().m2();
}
}
class D extends C<num> {
noCasts(t) {
f;
this.g;
this.m1();
m2;
super.f;
super.g;
super.m1;
super.m2();
}
}
D d;
C<Object> c;
C cD;
C<Null> cN;
F<Object> f;
F<Null> fN;
R<F<Object>> rf;
R<F<Null>> rfN;
R<R<F<Object>>> rrf;
R<R<F<Null>>> rrfN;
Object obj;
F<int> fi;
R<F<int>> rfi;
R<R<F<int>>> rrfi;
casts() {
c.f;
c.g;
c.m1;
c.m1();
c.m2();
fN = c.f;
fN = c.g;
rfN = c.m1;
rrfN = c.m2;
fN = c.m1();
rfN = c.m2();
f = c.f;
f = c.g;
rf = c.m1;
rrf = c.m2;
f = c.m1();
rf = c.m2();
c.m2()();
c.f(obj);
c.g(obj);
(c.f)(obj);
(c.g)(obj);
(c.m1)();
c.m1()(obj);
(c.m2)();
cD.f;
cD.g;
cD.m1;
cD.m1();
cD.m2();
}
noCasts() {
fi = d.f;
fi = d.g;
rfi = d.m1;
fi = d.m1();
rrfi = d.m2;
rfi = d.m2();
d.f(42);
d.g(42);
(d.f)(42);
(d.g)(42);
d.m1()(42);
d.m2()()(42);
cN.f;
cN.g;
cN.m1;
cN.m1();
cN.m2();
}
''');
var unit = (await computeAnalysisResult(source)).unit;
assertNoErrors(source);
void expectCast(Statement statement, bool hasCast) {
var value = (statement as ExpressionStatement).expression;
if (value is AssignmentExpression) {
value = (value as AssignmentExpression).rightHandSide;
}
while (value is FunctionExpressionInvocation) {
value = (value as FunctionExpressionInvocation).function;
}
while (value is ParenthesizedExpression) {
value = (value as ParenthesizedExpression).expression;
}
var isCallingGetter =
value is MethodInvocation && !value.methodName.name.startsWith('m');
var cast = isCallingGetter
? getImplicitOperationCast(value)
: getImplicitCast(value);
var castKind = isCallingGetter ? 'special cast' : 'cast';
expect(cast, hasCast ? isNotNull : isNull,
reason: '`$statement` should ' +
(hasCast ? '' : 'not ') +
'have a $castKind on `$value`.');
}
for (var s in AstFinder.getStatementsInMethod(unit, 'C', 'noCasts')) {
expectCast(s, false);
}
for (var s in AstFinder.getStatementsInMethod(unit, 'C', 'casts')) {
expectCast(s, true);
}
for (var s in AstFinder.getStatementsInMethod(unit, 'D', 'noCasts')) {
expectCast(s, false);
}
for (var s in AstFinder.getStatementsInTopLevelFunction(unit, 'noCasts')) {
expectCast(s, false);
}
for (var s in AstFinder.getStatementsInTopLevelFunction(unit, 'casts')) {
expectCast(s, true);
}
}
test_covarianceChecks_superclass() async {
var source = addSource(r'''
class C<T> {
add(T t) {}
forEach(void f(T t)) {}
}
class D {
add(int t) {}
forEach(void f(int t)) {}
}
class E extends D implements C<int> {}
''');
var unit = (await computeAnalysisResult(source)).unit;
assertNoErrors(source);
var cAdd = AstFinder.getMethodInClass(unit, "C", "add");
var covariantC = getClassCovariantParameters(AstFinder.getClass(unit, "C"));
expect(covariantC.toList(), [cAdd.element.parameters[0]]);
var dAdd = AstFinder.getMethodInClass(unit, "D", "add");
var covariantD = getClassCovariantParameters(AstFinder.getClass(unit, "D"));
expect(covariantD, null);
var classE = AstFinder.getClass(unit, "E");
var covariantE = getClassCovariantParameters(classE);
var superCovariantE = getSuperclassCovariantParameters(classE);
expect(covariantE.toList(), []);
expect(superCovariantE.toList(), [dAdd.element.parameters[0]]);
}
test_factoryConstructor_propagation() async {
String code = r'''
class A<T> {
factory A() { return new B(); }
}
class B<S> extends A<S> {}
''';
CompilationUnit unit = await resolveSource(code);
ConstructorDeclaration constructor =
AstFinder.getConstructorInClass(unit, "A", null);
BlockFunctionBody body = constructor.body;
ReturnStatement stmt = body.block.statements[0];
InstanceCreationExpression exp = stmt.expression;
ClassElement elementB = AstFinder.getClass(unit, "B").element;
ClassElement elementA = AstFinder.getClass(unit, "A").element;
expect(resolutionMap.typeForTypeName(exp.constructorName.type).element,
elementB);
_isInstantiationOf(_hasElement(elementB))(
[_isType(elementA.typeParameters[0].type)])(exp.staticType);
}
test_fieldDeclaration_propagation() async {
String code = r'''
class A {
List<String> f0 = ["hello"];
}
''';
CompilationUnit unit = await resolveSource(code);
VariableDeclaration field = AstFinder.getFieldInClass(unit, "A", "f0");
_isListOf(_isString)(field.initializer.staticType);
}
test_functionDeclaration_body_propagation() async {
String code = r'''
typedef T Function2<S, T>(S x);
List<int> test1() => [];
Function2<int, int> test2 (int x) {
Function2<String, int> inner() {
return (x) => x.length;
}
return (x) => x;
}
''';
CompilationUnit unit = await resolveSource(code);
Asserter<InterfaceType> assertListOfInt = _isListOf(_isInt);
FunctionDeclaration test1 = AstFinder.getTopLevelFunction(unit, "test1");
ExpressionFunctionBody body = test1.functionExpression.body;
assertListOfInt(body.expression.staticType);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test2");
FunctionDeclaration inner =
(statements[0] as FunctionDeclarationStatement).functionDeclaration;
BlockFunctionBody body0 = inner.functionExpression.body;
ReturnStatement return0 = body0.block.statements[0];
Expression anon0 = return0.expression;
FunctionType type0 = anon0.staticType;
expect(type0.returnType, typeProvider.intType);
expect(type0.normalParameterTypes[0], typeProvider.stringType);
FunctionExpression anon1 = (statements[1] as ReturnStatement).expression;
FunctionType type1 =
resolutionMap.elementDeclaredByFunctionExpression(anon1).type;
expect(type1.returnType, typeProvider.intType);
expect(type1.normalParameterTypes[0], typeProvider.intType);
}
test_functionLiteral_assignment_typedArguments() async {
String code = r'''
typedef T Function2<S, T>(S x);
void main () {
Function2<int, String> l0 = (int x) => null;
Function2<int, String> l1 = (int x) => "hello";
Function2<int, String> l2 = (String x) => "hello";
Function2<int, String> l3 = (int x) => 3;
Function2<int, String> l4 = (int x) {return 3;};
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
DartType literal(int i) {
VariableDeclarationStatement stmt = statements[i];
VariableDeclaration decl = stmt.variables.variables[0];
FunctionExpression exp = decl.initializer;
return resolutionMap.elementDeclaredByFunctionExpression(exp).type;
}
_isFunction2Of(_isInt, _isNull)(literal(0));
_isFunction2Of(_isInt, _isString)(literal(1));
_isFunction2Of(_isString, _isString)(literal(2));
_isFunction2Of(_isInt, _isString)(literal(3));
_isFunction2Of(_isInt, _isString)(literal(4));
}
test_functionLiteral_assignment_unTypedArguments() async {
String code = r'''
typedef T Function2<S, T>(S x);
void main () {
Function2<int, String> l0 = (x) => null;
Function2<int, String> l1 = (x) => "hello";
Function2<int, String> l2 = (x) => "hello";
Function2<int, String> l3 = (x) => 3;
Function2<int, String> l4 = (x) {return 3;};
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
DartType literal(int i) {
VariableDeclarationStatement stmt = statements[i];
VariableDeclaration decl = stmt.variables.variables[0];
FunctionExpression exp = decl.initializer;
return resolutionMap.elementDeclaredByFunctionExpression(exp).type;
}
_isFunction2Of(_isInt, _isNull)(literal(0));
_isFunction2Of(_isInt, _isString)(literal(1));
_isFunction2Of(_isInt, _isString)(literal(2));
_isFunction2Of(_isInt, _isString)(literal(3));
_isFunction2Of(_isInt, _isString)(literal(4));
}
test_functionLiteral_body_propagation() async {
String code = r'''
typedef T Function2<S, T>(S x);
void main () {
Function2<int, List<String>> l0 = (int x) => ["hello"];
Function2<int, List<String>> l1 = (String x) => ["hello"];
Function2<int, List<String>> l2 = (int x) => [3];
Function2<int, List<String>> l3 = (int x) {return [3];};
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
Expression functionReturnValue(int i) {
VariableDeclarationStatement stmt = statements[i];
VariableDeclaration decl = stmt.variables.variables[0];
FunctionExpression exp = decl.initializer;
FunctionBody body = exp.body;
if (body is ExpressionFunctionBody) {
return body.expression;
} else {
Statement stmt = (body as BlockFunctionBody).block.statements[0];
return (stmt as ReturnStatement).expression;
}
}
Asserter<InterfaceType> assertListOfString = _isListOf(_isString);
assertListOfString(functionReturnValue(0).staticType);
assertListOfString(functionReturnValue(1).staticType);
assertListOfString(functionReturnValue(2).staticType);
assertListOfString(functionReturnValue(3).staticType);
}
test_functionLiteral_functionExpressionInvocation_typedArguments() async {
String code = r'''
class Mapper<F, T> {
T map(T mapper(F x)) => mapper(null);
}
void main () {
(new Mapper<int, String>().map)((int x) => null);
(new Mapper<int, String>().map)((int x) => "hello");
(new Mapper<int, String>().map)((String x) => "hello");
(new Mapper<int, String>().map)((int x) => 3);
(new Mapper<int, String>().map)((int x) {return 3;});
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
DartType literal(int i) {
ExpressionStatement stmt = statements[i];
FunctionExpressionInvocation invk = stmt.expression;
FunctionExpression exp = invk.argumentList.arguments[0];
return resolutionMap.elementDeclaredByFunctionExpression(exp).type;
}
_isFunction2Of(_isInt, _isNull)(literal(0));
_isFunction2Of(_isInt, _isString)(literal(1));
_isFunction2Of(_isString, _isString)(literal(2));
_isFunction2Of(_isInt, _isString)(literal(3));
_isFunction2Of(_isInt, _isString)(literal(4));
}
test_functionLiteral_functionExpressionInvocation_unTypedArguments() async {
String code = r'''
class Mapper<F, T> {
T map(T mapper(F x)) => mapper(null);
}
void main () {
(new Mapper<int, String>().map)((x) => null);
(new Mapper<int, String>().map)((x) => "hello");
(new Mapper<int, String>().map)((x) => "hello");
(new Mapper<int, String>().map)((x) => 3);
(new Mapper<int, String>().map)((x) {return 3;});
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
DartType literal(int i) {
ExpressionStatement stmt = statements[i];
FunctionExpressionInvocation invk = stmt.expression;
FunctionExpression exp = invk.argumentList.arguments[0];
return resolutionMap.elementDeclaredByFunctionExpression(exp).type;
}
_isFunction2Of(_isInt, _isNull)(literal(0));
_isFunction2Of(_isInt, _isString)(literal(1));
_isFunction2Of(_isInt, _isString)(literal(2));
_isFunction2Of(_isInt, _isString)(literal(3));
_isFunction2Of(_isInt, _isString)(literal(4));
}
test_functionLiteral_functionInvocation_typedArguments() async {
String code = r'''
String map(String mapper(int x)) => mapper(null);
void main () {
map((int x) => null);
map((int x) => "hello");
map((String x) => "hello");
map((int x) => 3);
map((int x) {return 3;});
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
DartType literal(int i) {
ExpressionStatement stmt = statements[i];
MethodInvocation invk = stmt.expression;
FunctionExpression exp = invk.argumentList.arguments[0];
return resolutionMap.elementDeclaredByFunctionExpression(exp).type;
}
_isFunction2Of(_isInt, _isNull)(literal(0));
_isFunction2Of(_isInt, _isString)(literal(1));
_isFunction2Of(_isString, _isString)(literal(2));
_isFunction2Of(_isInt, _isString)(literal(3));
_isFunction2Of(_isInt, _isString)(literal(4));
}
test_functionLiteral_functionInvocation_unTypedArguments() async {
String code = r'''
String map(String mapper(int x)) => mapper(null);
void main () {
map((x) => null);
map((x) => "hello");
map((x) => "hello");
map((x) => 3);
map((x) {return 3;});
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
DartType literal(int i) {
ExpressionStatement stmt = statements[i];
MethodInvocation invk = stmt.expression;
FunctionExpression exp = invk.argumentList.arguments[0];
return resolutionMap.elementDeclaredByFunctionExpression(exp).type;
}
_isFunction2Of(_isInt, _isNull)(literal(0));
_isFunction2Of(_isInt, _isString)(literal(1));
_isFunction2Of(_isInt, _isString)(literal(2));
_isFunction2Of(_isInt, _isString)(literal(3));
_isFunction2Of(_isInt, _isString)(literal(4));
}
test_functionLiteral_methodInvocation_typedArguments() async {
String code = r'''
class Mapper<F, T> {
T map(T mapper(F x)) => mapper(null);
}
void main () {
new Mapper<int, String>().map((int x) => null);
new Mapper<int, String>().map((int x) => "hello");
new Mapper<int, String>().map((String x) => "hello");
new Mapper<int, String>().map((int x) => 3);
new Mapper<int, String>().map((int x) {return 3;});
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
DartType literal(int i) {
ExpressionStatement stmt = statements[i];
MethodInvocation invk = stmt.expression;
FunctionExpression exp = invk.argumentList.arguments[0];
return resolutionMap.elementDeclaredByFunctionExpression(exp).type;
}
_isFunction2Of(_isInt, _isNull)(literal(0));
_isFunction2Of(_isInt, _isString)(literal(1));
_isFunction2Of(_isString, _isString)(literal(2));
_isFunction2Of(_isInt, _isString)(literal(3));
_isFunction2Of(_isInt, _isString)(literal(4));
}
test_functionLiteral_methodInvocation_unTypedArguments() async {
String code = r'''
class Mapper<F, T> {
T map(T mapper(F x)) => mapper(null);
}
void main () {
new Mapper<int, String>().map((x) => null);
new Mapper<int, String>().map((x) => "hello");
new Mapper<int, String>().map((x) => "hello");
new Mapper<int, String>().map((x) => 3);
new Mapper<int, String>().map((x) {return 3;});
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
DartType literal(int i) {
ExpressionStatement stmt = statements[i];
MethodInvocation invk = stmt.expression;
FunctionExpression exp = invk.argumentList.arguments[0];
return resolutionMap.elementDeclaredByFunctionExpression(exp).type;
}
_isFunction2Of(_isInt, _isNull)(literal(0));
_isFunction2Of(_isInt, _isString)(literal(1));
_isFunction2Of(_isInt, _isString)(literal(2));
_isFunction2Of(_isInt, _isString)(literal(3));
_isFunction2Of(_isInt, _isString)(literal(4));
}
test_functionLiteral_unTypedArgument_propagation() async {
String code = r'''
typedef T Function2<S, T>(S x);
void main () {
Function2<int, int> l0 = (x) => x;
Function2<int, int> l1 = (x) => x+1;
Function2<int, String> l2 = (x) => x;
Function2<int, String> l3 = (x) => x.toLowerCase();
Function2<String, String> l4 = (x) => x.toLowerCase();
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
Expression functionReturnValue(int i) {
VariableDeclarationStatement stmt = statements[i];
VariableDeclaration decl = stmt.variables.variables[0];
FunctionExpression exp = decl.initializer;
FunctionBody body = exp.body;
if (body is ExpressionFunctionBody) {
return body.expression;
} else {
Statement stmt = (body as BlockFunctionBody).block.statements[0];
return (stmt as ReturnStatement).expression;
}
}
expect(functionReturnValue(0).staticType, typeProvider.intType);
expect(functionReturnValue(1).staticType, typeProvider.intType);
expect(functionReturnValue(2).staticType, typeProvider.intType);
expect(functionReturnValue(3).staticType, typeProvider.dynamicType);
expect(functionReturnValue(4).staticType, typeProvider.stringType);
}
test_futureOr_assignFromFuture() async {
// Test a Future<T> can be assigned to FutureOr<T>.
MethodInvocation invoke = await _testFutureOr(r'''
FutureOr<T> mk<T>(Future<T> x) => x;
test() => mk(new Future<int>.value(42));
''');
_isFutureOrOfInt(invoke.staticType);
}
test_futureOr_assignFromValue() async {
// Test a T can be assigned to FutureOr<T>.
MethodInvocation invoke = await _testFutureOr(r'''
FutureOr<T> mk<T>(T x) => x;
test() => mk(42);
''');
_isFutureOrOfInt(invoke.staticType);
}
test_futureOr_asyncExpressionBody() async {
// A FutureOr<T> can be used as the expression body for an async function
MethodInvocation invoke = await _testFutureOr(r'''
Future<T> mk<T>(FutureOr<T> x) async => x;
test() => mk(42);
''');
_isFutureOfInt(invoke.staticType);
}
test_futureOr_asyncReturn() async {
// A FutureOr<T> can be used as the return value for an async function
MethodInvocation invoke = await _testFutureOr(r'''
Future<T> mk<T>(FutureOr<T> x) async { return x; }
test() => mk(42);
''');
_isFutureOfInt(invoke.staticType);
}
test_futureOr_await() async {
// Test a FutureOr<T> can be awaited.
MethodInvocation invoke = await _testFutureOr(r'''
Future<T> mk<T>(FutureOr<T> x) async => await x;
test() => mk(42);
''');
_isFutureOfInt(invoke.staticType);
}
test_futureOr_downwards1() async {
// Test that downwards inference interacts correctly with FutureOr
// parameters.
MethodInvocation invoke = await _testFutureOr(r'''
Future<T> mk<T>(FutureOr<T> x) => null;
Future<int> test() => mk(new Future<int>.value(42));
''');
_isFutureOfInt(invoke.staticType);
}
test_futureOr_downwards2() async {
// Test that downwards inference interacts correctly with FutureOr
// parameters when the downwards context is FutureOr
MethodInvocation invoke = await _testFutureOr(r'''
Future<T> mk<T>(FutureOr<T> x) => null;
FutureOr<int> test() => mk(new Future<int>.value(42));
''');
_isFutureOfInt(invoke.staticType);
}
test_futureOr_downwards3() async {
// Test that downwards inference correctly propogates into
// arguments.
MethodInvocation invoke = await _testFutureOr(r'''
Future<T> mk<T>(FutureOr<T> x) => null;
Future<int> test() => mk(new Future.value(42));
''');
_isFutureOfInt(invoke.staticType);
_isFutureOfInt(invoke.argumentList.arguments[0].staticType);
}
test_futureOr_downwards4() async {
// Test that downwards inference interacts correctly with FutureOr
// parameters when the downwards context is FutureOr
MethodInvocation invoke = await _testFutureOr(r'''
Future<T> mk<T>(FutureOr<T> x) => null;
FutureOr<int> test() => mk(new Future.value(42));
''');
_isFutureOfInt(invoke.staticType);
_isFutureOfInt(invoke.argumentList.arguments[0].staticType);
}
test_futureOr_downwards5() async {
// Test that downwards inference correctly pins the type when it
// comes from a FutureOr
MethodInvocation invoke = await _testFutureOr(r'''
Future<T> mk<T>(FutureOr<T> x) => null;
FutureOr<num> test() => mk(new Future.value(42));
''');
_isFutureOf([_isNum])(invoke.staticType);
_isFutureOf([_isNum])(invoke.argumentList.arguments[0].staticType);
}
test_futureOr_downwards6() async {
// Test that downwards inference doesn't decompose FutureOr
// when instantiating type variables.
MethodInvocation invoke = await _testFutureOr(r'''
T mk<T>(T x) => null;
FutureOr<int> test() => mk(new Future.value(42));
''');
_isFutureOrOfInt(invoke.staticType);
_isFutureOfInt(invoke.argumentList.arguments[0].staticType);
}
test_futureOr_downwards7() async {
// Test that downwards inference incorporates bounds correctly
// when instantiating type variables.
MethodInvocation invoke = await _testFutureOr(r'''
T mk<T extends Future<int>>(T x) => null;
FutureOr<int> test() => mk(new Future.value(42));
''');
_isFutureOfInt(invoke.staticType);
_isFutureOfInt(invoke.argumentList.arguments[0].staticType);
}
test_futureOr_downwards8() async {
// Test that downwards inference incorporates bounds correctly
// when instantiating type variables.
// TODO(leafp): I think this should pass once the inference changes
// that jmesserly is adding are landed.
MethodInvocation invoke = await _testFutureOr(r'''
T mk<T extends Future<Object>>(T x) => null;
FutureOr<int> test() => mk(new Future.value(42));
''');
_isFutureOfInt(invoke.staticType);
_isFutureOfInt(invoke.argumentList.arguments[0].staticType);
}
test_futureOr_downwards9() async {
// Test that downwards inference decomposes correctly with
// other composite types
MethodInvocation invoke = await _testFutureOr(r'''
List<T> mk<T>(T x) => null;
FutureOr<List<int>> test() => mk(3);
''');
_isListOf(_isInt)(invoke.staticType);
_isInt(invoke.argumentList.arguments[0].staticType);
}
test_futureOr_methods1() async {
// Test that FutureOr has the Object methods
MethodInvocation invoke = await _testFutureOr(r'''
dynamic test(FutureOr<int> x) => x.toString();
''');
_isString(invoke.staticType);
}
test_futureOr_methods2() async {
// Test that FutureOr does not have the constituent type methods
MethodInvocation invoke = await _testFutureOr(r'''
dynamic test(FutureOr<int> x) => x.abs();
''', errors: [StaticTypeWarningCode.UNDEFINED_METHOD]);
_isDynamic(invoke.staticType);
}
test_futureOr_methods3() async {
// Test that FutureOr does not have the Future type methods
MethodInvocation invoke = await _testFutureOr(r'''
dynamic test(FutureOr<int> x) => x.then((x) => x);
''', errors: [StaticTypeWarningCode.UNDEFINED_METHOD]);
_isDynamic(invoke.staticType);
}
test_futureOr_methods4() async {
// Test that FutureOr<dynamic> does not have all methods
MethodInvocation invoke = await _testFutureOr(r'''
dynamic test(FutureOr<dynamic> x) => x.abs();
''', errors: [StaticTypeWarningCode.UNDEFINED_METHOD]);
_isDynamic(invoke.staticType);
}
test_futureOr_no_return() async {
MethodInvocation invoke = await _testFutureOr(r'''
FutureOr<T> mk<T>(Future<T> x) => x;
Future<int> f;
test() => f.then((int x) {});
''');
_isFunction2Of(_isInt, _isNull)(
invoke.argumentList.arguments[0].staticType);
_isFutureOfNull(invoke.staticType);
}
test_futureOr_no_return_value() async {
MethodInvocation invoke = await _testFutureOr(r'''
FutureOr<T> mk<T>(Future<T> x) => x;
Future<int> f;
test() => f.then((int x) {return;});
''');
_isFunction2Of(_isInt, _isNull)(
invoke.argumentList.arguments[0].staticType);
_isFutureOfNull(invoke.staticType);
}
test_futureOr_return_null() async {
MethodInvocation invoke = await _testFutureOr(r'''
FutureOr<T> mk<T>(Future<T> x) => x;
Future<int> f;
test() => f.then((int x) {return null;});
''');
_isFunction2Of(_isInt, _isNull)(
invoke.argumentList.arguments[0].staticType);
_isFutureOfNull(invoke.staticType);
}
test_futureOr_upwards1() async {
// Test that upwards inference correctly prefers to instantiate type
// variables with the "smaller" solution when both are possible.
MethodInvocation invoke = await _testFutureOr(r'''
Future<T> mk<T>(FutureOr<T> x) => null;
dynamic test() => mk(new Future<int>.value(42));
''');
_isFutureOfInt(invoke.staticType);
}
test_futureOr_upwards2() async {
// Test that upwards inference fails when the solution doesn't
// match the bound.
MethodInvocation invoke = await _testFutureOr(r'''
Future<T> mk<T extends Future<Object>>(FutureOr<T> x) => null;
dynamic test() => mk(new Future<int>.value(42));
''', errors: [StrongModeCode.COULD_NOT_INFER]);
_isFutureOfInt(invoke.staticType);
}
test_futureOrNull_no_return() async {
MethodInvocation invoke = await _testFutureOr(r'''
FutureOr<T> mk<T>(Future<T> x) => x;
Future<int> f;
test() => f.then<Null>((int x) {});
''');
_isFunction2Of(_isInt, _isNull)(
invoke.argumentList.arguments[0].staticType);
_isFutureOfNull(invoke.staticType);
}
test_futureOrNull_no_return_value() async {
MethodInvocation invoke = await _testFutureOr(r'''
FutureOr<T> mk<T>(Future<T> x) => x;
Future<int> f;
test() => f.then<Null>((int x) {return;});
''');
_isFunction2Of(_isInt, _isNull)(
invoke.argumentList.arguments[0].staticType);
_isFutureOfNull(invoke.staticType);
}
test_futureOrNull_return_null() async {
MethodInvocation invoke = await _testFutureOr(r'''
FutureOr<T> mk<T>(Future<T> x) => x;
Future<int> f;
test() => f.then<Null>((int x) { return null;});
''');
_isFunction2Of(_isInt, _isNull)(
invoke.argumentList.arguments[0].staticType);
_isFutureOfNull(invoke.staticType);
}
test_generic_partial() async {
// Test that upward and downward type inference handles partial
// type schemas correctly. Downwards inference in a partial context
// (e.g. Map<String, ?>) should still allow upwards inference to fill
// in the missing information.
String code = r'''
class A<T> {
A(T x);
A.fromA(A<T> a) {}
A.fromMap(Map<String, T> m) {}
A.fromList(List<T> m) {}
A.fromT(T t) {}
A.fromB(B<T, String> a) {}
}
class B<S, T> {
B(S s);
}
void test() {
var a0 = new A.fromA(new A(3));
var a1 = new A.fromMap({'hello' : 3});
var a2 = new A.fromList([3]);
var a3 = new A.fromT(3);
var a4 = new A.fromB(new B(3));
}
''';
CompilationUnit unit = await resolveSource(code);
Element elementA = AstFinder.getClass(unit, "A").element;
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test");
void check(int i) {
VariableDeclarationStatement stmt = statements[i];
VariableDeclaration decl = stmt.variables.variables[0];
Expression init = decl.initializer;
_isInstantiationOf(_hasElement(elementA))([_isInt])(init.staticType);
}
for (var i = 0; i < 5; i++) check(i);
}
test_inferConstructor_unknownTypeLowerBound() async {
Source source = addSource(r'''
class C<T> {
C(void callback(List<T> a));
}
test() {
// downwards inference pushes List<?> and in parameter position this
// becomes inferred as List<Null>.
var c = new C((items) {});
}
''');
CompilationUnit unit = (await computeAnalysisResult(source)).unit;
assertNoErrors(source);
verify([source]);
DartType cType = findLocalVariable(unit, 'c').type;
Element elementC = AstFinder.getClass(unit, "C").element;
_isInstantiationOf(_hasElement(elementC))([_isDynamic])(cType);
}
test_inference_error_arguments() async {
Source source = addSource(r'''
typedef R F<T, R>(T t);
F<T, T> g<T>(F<T, T> f) => (x) => f(f(x));
test() {
var h = g((int x) => 42.0);
}
''');
await computeAnalysisResult(source);
_expectInferenceError(source, [
StrongModeCode.COULD_NOT_INFER,
StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE
], r'''
Couldn't infer type parameter 'T'.
Tried to infer 'double' for 'T' which doesn't work:
Parameter 'f' declared as '(T) → T'
but argument is '(int) → double'.
Consider passing explicit type argument(s) to the generic.
''');
}
test_inference_error_arguments2() async {
Source source = addSource(r'''
typedef R F<T, R>(T t);
F<T, T> g<T>(F<T, T> a, F<T, T> b) => (x) => a(b(x));
test() {
var h = g((int x) => 42.0, (double x) => 42);
}
''');
await computeAnalysisResult(source);
_expectInferenceError(source, [
StrongModeCode.COULD_NOT_INFER,
StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE,
StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE
], r'''
Couldn't infer type parameter 'T'.
Tried to infer 'num' for 'T' which doesn't work:
Parameter 'a' declared as '(T) → T'
but argument is '(int) → double'.
Parameter 'b' declared as '(T) → T'
but argument is '(double) → int'.
Consider passing explicit type argument(s) to the generic.
''');
}
test_inference_error_extendsFromReturn() async {
// This is not an inference error because we successfully infer Null.
Source source = addSource(r'''
T max<T extends num>(T x, T y) => x;
test() {
String hello = max(1, 2);
}
''');
var analysisResult = await computeAnalysisResult(source);
assertErrors(source, [
StrongModeCode.INVALID_CAST_LITERAL,
StrongModeCode.INVALID_CAST_LITERAL
]);
var unit = analysisResult.unit;
var h = (AstFinder.getStatementsInTopLevelFunction(unit, "test")[0]
as VariableDeclarationStatement)
.variables
.variables[0];
var call = h.initializer as MethodInvocation;
expect(call.staticInvokeType.toString(), '(Null, Null) → Null');
}
test_inference_error_extendsFromReturn2() async {
Source source = addSource(r'''
typedef R F<T, R>(T t);
F<T, T> g<T extends num>() => (y) => y;
test() {
F<String, String> hello = g();
}
''');
await computeAnalysisResult(source);
_expectInferenceError(source, [
StrongModeCode.COULD_NOT_INFER,
], r'''
Couldn't infer type parameter 'T'.
Tried to infer 'String' for 'T' which doesn't work:
Type parameter 'T' declared to extend 'num'.
The type 'String' was inferred from:
Return type declared as '(T) → T'
used where '(String) → String' is required.
Consider passing explicit type argument(s) to the generic.
''');
}
test_inference_error_genericFunction() async {
Source source = addSource(r'''
T max<T extends num>(T x, T y) => x < y ? y : x;
abstract class Iterable<T> {
T get first;
S fold<S>(S s, S f(S s, T t));
}
test(Iterable values) {
num n = values.fold(values.first as num, max);
}
''');
await computeAnalysisResult(source);
_expectInferenceError(source, [
StrongModeCode.COULD_NOT_INFER,
StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE
], r'''
Couldn't infer type parameter 'T'.
Tried to infer 'dynamic' for 'T' which doesn't work:
Function type declared as '<T extends num>(T, T) → T'
used where '(num, dynamic) → num' is required.
Consider passing explicit type argument(s) to the generic.
''');
}
test_inference_error_returnContext() async {
Source source = addSource(r'''
typedef R F<T, R>(T t);
F<T, T> g<T>(T t) => (x) => t;
test() {
F<num, int> h = g(42);
}
''');
await computeAnalysisResult(source);
_expectInferenceError(source, [StrongModeCode.COULD_NOT_INFER], r'''
Couldn't infer type parameter 'T'.
Tried to infer 'num' for 'T' which doesn't work:
Return type declared as '(T) → T'
used where '(num) → int' is required.
Consider passing explicit type argument(s) to the generic.
''');
}
test_inference_hints() async {
Source source = addSource(r'''
void main () {
var x = 3;
List<int> l0 = [];
}
''');
await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
}
test_inference_simplePolymorphicRecursion_function() async {
// Regression test for https://github.com/dart-lang/sdk/issues/30980
// Check that inference works properly when inferring the type argument
// for a self-recursive call with a function type
var source = addSource(r'''
void _mergeSort<T>(T Function(T) list, int compare(T a, T b), T Function(T) target) {
_mergeSort(list, compare, target);
_mergeSort(list, compare, list);
_mergeSort(target, compare, target);
_mergeSort(target, compare, list);
}
''');
var analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
var unit = analysisResult.unit;
var body = (AstFinder
.getTopLevelFunction(unit, '_mergeSort')
.functionExpression
.body as BlockFunctionBody);
var stmts = body.block.statements;
for (ExpressionStatement stmt in stmts) {
MethodInvocation invoke = stmt.expression;
ParameterizedType fType = invoke.staticInvokeType;
expect(fType.typeArguments[0].toString(), 'T');
}
}
test_inference_simplePolymorphicRecursion_interface() async {
// Regression test for https://github.com/dart-lang/sdk/issues/30980
// Check that inference works properly when inferring the type argument
// for a self-recursive call with an interface type
var source = addSource(r'''
void _mergeSort<T>(List<T> list, int compare(T a, T b), List<T> target) {
_mergeSort(list, compare, target);
_mergeSort(list, compare, list);
_mergeSort(target, compare, target);
_mergeSort(target, compare, list);
}
''');
var analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
var unit = analysisResult.unit;
var body = (AstFinder
.getTopLevelFunction(unit, '_mergeSort')
.functionExpression
.body as BlockFunctionBody);
var stmts = body.block.statements;
for (ExpressionStatement stmt in stmts) {
MethodInvocation invoke = stmt.expression;
ParameterizedType fType = invoke.staticInvokeType;
expect(fType.typeArguments[0].toString(), 'T');
}
}
test_inference_simplePolymorphicRecursion_simple() async {
// Regression test for https://github.com/dart-lang/sdk/issues/30980
// Check that inference works properly when inferring the type argument
// for a self-recursive call with a simple type parameter
var source = addSource(r'''
void _mergeSort<T>(T list, int compare(T a, T b), T target) {
_mergeSort(list, compare, target);
_mergeSort(list, compare, list);
_mergeSort(target, compare, target);
_mergeSort(target, compare, list);
}
''');
var analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
var unit = analysisResult.unit;
var body = (AstFinder
.getTopLevelFunction(unit, '_mergeSort')
.functionExpression
.body as BlockFunctionBody);
var stmts = body.block.statements;
for (ExpressionStatement stmt in stmts) {
MethodInvocation invoke = stmt.expression;
ParameterizedType fType = invoke.staticInvokeType;
expect(fType.typeArguments[0].toString(), 'T');
}
}
test_inferGenericInstantiation() async {
// Verify that we don't infer '?` when we instantiate a generic function.
var source = addSource(r'''
T f<T>(T x(T t)) => x(null);
S g<S>(S s) => s;
test() {
var h = f(g);
}
''');
var analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
var unit = analysisResult.unit;
var h = (AstFinder.getStatementsInTopLevelFunction(unit, "test")[0]
as VariableDeclarationStatement)
.variables
.variables[0];
_isDynamic(h.element.type);
var fCall = h.initializer as MethodInvocation;
expect(
fCall.staticInvokeType.toString(), '((dynamic) → dynamic) → dynamic');
var g = fCall.argumentList.arguments[0];
expect(g.staticType.toString(), '(dynamic) → dynamic');
}
test_inferGenericInstantiation2() async {
// Verify the behavior when we cannot infer an instantiation due to invalid
// constraints from an outer generic method.
var source = addSource(r'''
T max<T extends num>(T x, T y) => x < y ? y : x;
abstract class Iterable<T> {
T get first;
S fold<S>(S s, S f(S s, T t));
}
num test(Iterable values) => values.fold(values.first as num, max);
''');
var analysisResult = await computeAnalysisResult(source);
assertErrors(source, [
StrongModeCode.COULD_NOT_INFER,
StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE
]);
verify([source]);
var unit = analysisResult.unit;
var fold = (AstFinder
.getTopLevelFunction(unit, 'test')
.functionExpression
.body as ExpressionFunctionBody)
.expression as MethodInvocation;
expect(
fold.staticInvokeType.toString(), '(num, (num, dynamic) → num) → num');
var max = fold.argumentList.arguments[1];
// TODO(jmesserly): arguably (num, num) → num is better here.
expect(max.staticType.toString(), '(dynamic, dynamic) → dynamic');
}
test_inferredFieldDeclaration_propagation() async {
// Regression test for https://github.com/dart-lang/sdk/issues/25546
String code = r'''
abstract class A {
Map<int, List<int>> get map;
}
class B extends A {
var map = { 42: [] };
}
class C extends A {
get map => { 43: [] };
}
''';
CompilationUnit unit = await resolveSource(code);
Asserter<InterfaceType> assertListOfInt = _isListOf(_isInt);
Asserter<InterfaceType> assertMapOfIntToListOfInt =
_isMapOf(_isInt, (DartType type) => assertListOfInt(type));
VariableDeclaration mapB = AstFinder.getFieldInClass(unit, "B", "map");
MethodDeclaration mapC = AstFinder.getMethodInClass(unit, "C", "map");
assertMapOfIntToListOfInt(
resolutionMap.elementDeclaredByVariableDeclaration(mapB).type);
assertMapOfIntToListOfInt(
resolutionMap.elementDeclaredByMethodDeclaration(mapC).returnType);
MapLiteral mapLiteralB = mapB.initializer;
MapLiteral mapLiteralC = (mapC.body as ExpressionFunctionBody).expression;
assertMapOfIntToListOfInt(mapLiteralB.staticType);
assertMapOfIntToListOfInt(mapLiteralC.staticType);
ListLiteral listLiteralB = mapLiteralB.entries[0].value;
ListLiteral listLiteralC = mapLiteralC.entries[0].value;
assertListOfInt(listLiteralB.staticType);
assertListOfInt(listLiteralC.staticType);
}
test_instanceCreation() async {
String code = r'''
class A<S, T> {
S x;
T y;
A(this.x, this.y);
A.named(this.x, this.y);
}
class B<S, T> extends A<T, S> {
B(S y, T x) : super(x, y);
B.named(S y, T x) : super.named(x, y);
}
class C<S> extends B<S, S> {
C(S a) : super(a, a);
C.named(S a) : super.named(a, a);
}
class D<S, T> extends B<T, int> {
D(T a) : super(a, 3);
D.named(T a) : super.named(a, 3);
}
class E<S, T> extends A<C<S>, T> {
E(T a) : super(null, a);
}
class F<S, T> extends A<S, T> {
F(S x, T y, {List<S> a, List<T> b}) : super(x, y);
F.named(S x, T y, [S a, T b]) : super(a, b);
}
void test0() {
A<int, String> a0 = new A(3, "hello");
A<int, String> a1 = new A.named(3, "hello");
A<int, String> a2 = new A<int, String>(3, "hello");
A<int, String> a3 = new A<int, String>.named(3, "hello");
A<int, String> a4 = new A<int, dynamic>(3, "hello");
A<int, String> a5 = new A<dynamic, dynamic>.named(3, "hello");
}
void test1() {
A<int, String> a0 = new A("hello", 3);
A<int, String> a1 = new A.named("hello", 3);
}
void test2() {
A<int, String> a0 = new B("hello", 3);
A<int, String> a1 = new B.named("hello", 3);
A<int, String> a2 = new B<String, int>("hello", 3);
A<int, String> a3 = new B<String, int>.named("hello", 3);
A<int, String> a4 = new B<String, dynamic>("hello", 3);
A<int, String> a5 = new B<dynamic, dynamic>.named("hello", 3);
}
void test3() {
A<int, String> a0 = new B(3, "hello");
A<int, String> a1 = new B.named(3, "hello");
}
void test4() {
A<int, int> a0 = new C(3);
A<int, int> a1 = new C.named(3);
A<int, int> a2 = new C<int>(3);
A<int, int> a3 = new C<int>.named(3);
A<int, int> a4 = new C<dynamic>(3);
A<int, int> a5 = new C<dynamic>.named(3);
}
void test5() {
A<int, int> a0 = new C("hello");
A<int, int> a1 = new C.named("hello");
}
void test6() {
A<int, String> a0 = new D("hello");
A<int, String> a1 = new D.named("hello");
A<int, String> a2 = new D<int, String>("hello");
A<int, String> a3 = new D<String, String>.named("hello");
A<int, String> a4 = new D<num, dynamic>("hello");
A<int, String> a5 = new D<dynamic, dynamic>.named("hello");
}
void test7() {
A<int, String> a0 = new D(3);
A<int, String> a1 = new D.named(3);
}
void test8() {
A<C<int>, String> a0 = new E("hello");
}
void test9() { // Check named and optional arguments
A<int, String> a0 = new F(3, "hello", a: [3], b: ["hello"]);
A<int, String> a1 = new F(3, "hello", a: ["hello"], b:[3]);
A<int, String> a2 = new F.named(3, "hello", 3, "hello");
A<int, String> a3 = new F.named(3, "hello");
A<int, String> a4 = new F.named(3, "hello", "hello", 3);
A<int, String> a5 = new F.named(3, "hello", "hello");
}''';
CompilationUnit unit = await resolveSource(code);
Expression rhs(VariableDeclarationStatement stmt) {
VariableDeclaration decl = stmt.variables.variables[0];
Expression exp = decl.initializer;
return exp;
}
void hasType(Asserter<DartType> assertion, Expression exp) =>
assertion(exp.staticType);
Element elementA = AstFinder.getClass(unit, "A").element;
Element elementB = AstFinder.getClass(unit, "B").element;
Element elementC = AstFinder.getClass(unit, "C").element;
Element elementD = AstFinder.getClass(unit, "D").element;
Element elementE = AstFinder.getClass(unit, "E").element;
Element elementF = AstFinder.getClass(unit, "F").element;
AsserterBuilder<List<Asserter<DartType>>, DartType> assertAOf =
_isInstantiationOf(_hasElement(elementA));
AsserterBuilder<List<Asserter<DartType>>, DartType> assertBOf =
_isInstantiationOf(_hasElement(elementB));
AsserterBuilder<List<Asserter<DartType>>, DartType> assertCOf =
_isInstantiationOf(_hasElement(elementC));
AsserterBuilder<List<Asserter<DartType>>, DartType> assertDOf =
_isInstantiationOf(_hasElement(elementD));
AsserterBuilder<List<Asserter<DartType>>, DartType> assertEOf =
_isInstantiationOf(_hasElement(elementE));
AsserterBuilder<List<Asserter<DartType>>, DartType> assertFOf =
_isInstantiationOf(_hasElement(elementF));
{
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test0");
hasType(assertAOf([_isInt, _isString]), rhs(statements[0]));
hasType(assertAOf([_isInt, _isString]), rhs(statements[0]));
hasType(assertAOf([_isInt, _isString]), rhs(statements[1]));
hasType(assertAOf([_isInt, _isString]), rhs(statements[2]));
hasType(assertAOf([_isInt, _isString]), rhs(statements[3]));
hasType(assertAOf([_isInt, _isDynamic]), rhs(statements[4]));
hasType(assertAOf([_isDynamic, _isDynamic]), rhs(statements[5]));
}
{
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test1");
hasType(assertAOf([_isInt, _isString]), rhs(statements[0]));
hasType(assertAOf([_isInt, _isString]), rhs(statements[1]));
}
{
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test2");
hasType(assertBOf([_isString, _isInt]), rhs(statements[0]));
hasType(assertBOf([_isString, _isInt]), rhs(statements[1]));
hasType(assertBOf([_isString, _isInt]), rhs(statements[2]));
hasType(assertBOf([_isString, _isInt]), rhs(statements[3]));
hasType(assertBOf([_isString, _isDynamic]), rhs(statements[4]));
hasType(assertBOf([_isDynamic, _isDynamic]), rhs(statements[5]));
}
{
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test3");
hasType(assertBOf([_isString, _isInt]), rhs(statements[0]));
hasType(assertBOf([_isString, _isInt]), rhs(statements[1]));
}
{
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test4");
hasType(assertCOf([_isInt]), rhs(statements[0]));
hasType(assertCOf([_isInt]), rhs(statements[1]));
hasType(assertCOf([_isInt]), rhs(statements[2]));
hasType(assertCOf([_isInt]), rhs(statements[3]));
hasType(assertCOf([_isDynamic]), rhs(statements[4]));
hasType(assertCOf([_isDynamic]), rhs(statements[5]));
}
{
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test5");
hasType(assertCOf([_isInt]), rhs(statements[0]));
hasType(assertCOf([_isInt]), rhs(statements[1]));
}
{
// The first type parameter is not constrained by the
// context. We could choose a tighter type, but currently
// we just use dynamic.
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test6");
hasType(assertDOf([_isDynamic, _isString]), rhs(statements[0]));
hasType(assertDOf([_isDynamic, _isString]), rhs(statements[1]));
hasType(assertDOf([_isInt, _isString]), rhs(statements[2]));
hasType(assertDOf([_isString, _isString]), rhs(statements[3]));
hasType(assertDOf([_isNum, _isDynamic]), rhs(statements[4]));
hasType(assertDOf([_isDynamic, _isDynamic]), rhs(statements[5]));
}
{
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test7");
hasType(assertDOf([_isDynamic, _isString]), rhs(statements[0]));
hasType(assertDOf([_isDynamic, _isString]), rhs(statements[1]));
}
{
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test8");
hasType(assertEOf([_isInt, _isString]), rhs(statements[0]));
}
{
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "test9");
hasType(assertFOf([_isInt, _isString]), rhs(statements[0]));
hasType(assertFOf([_isInt, _isString]), rhs(statements[1]));
hasType(assertFOf([_isInt, _isString]), rhs(statements[2]));
hasType(assertFOf([_isInt, _isString]), rhs(statements[3]));
hasType(assertFOf([_isInt, _isString]), rhs(statements[4]));
hasType(assertFOf([_isInt, _isString]), rhs(statements[5]));
}
}
test_listLiteral_nested() async {
String code = r'''
void main () {
List<List<int>> l0 = [[]];
Iterable<List<int>> l1 = [[3]];
Iterable<List<int>> l2 = [[3], [4]];
List<List<int>> l3 = [["hello", 3], []];
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
ListLiteral literal(int i) {
VariableDeclarationStatement stmt = statements[i];
VariableDeclaration decl = stmt.variables.variables[0];
ListLiteral exp = decl.initializer;
return exp;
}
Asserter<InterfaceType> assertListOfInt = _isListOf(_isInt);
Asserter<InterfaceType> assertListOfListOfInt =
_isListOf((DartType type) => assertListOfInt(type));
assertListOfListOfInt(literal(0).staticType);
assertListOfListOfInt(literal(1).staticType);
assertListOfListOfInt(literal(2).staticType);
assertListOfListOfInt(literal(3).staticType);
assertListOfInt(literal(1).elements[0].staticType);
assertListOfInt(literal(2).elements[0].staticType);
assertListOfInt(literal(3).elements[0].staticType);
}
test_listLiteral_simple() async {
String code = r'''
void main () {
List<int> l0 = [];
List<int> l1 = [3];
List<int> l2 = ["hello"];
List<int> l3 = ["hello", 3];
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
DartType literal(int i) {
VariableDeclarationStatement stmt = statements[i];
VariableDeclaration decl = stmt.variables.variables[0];
ListLiteral exp = decl.initializer;
return exp.staticType;
}
Asserter<InterfaceType> assertListOfInt = _isListOf(_isInt);
assertListOfInt(literal(0));
assertListOfInt(literal(1));
assertListOfInt(literal(2));
assertListOfInt(literal(3));
}
test_listLiteral_simple_const() async {
String code = r'''
void main () {
const List<int> c0 = const [];
const List<int> c1 = const [3];
const List<int> c2 = const ["hello"];
const List<int> c3 = const ["hello", 3];
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
DartType literal(int i) {
VariableDeclarationStatement stmt = statements[i];
VariableDeclaration decl = stmt.variables.variables[0];
ListLiteral exp = decl.initializer;
return exp.staticType;
}
Asserter<InterfaceType> assertListOfInt = _isListOf(_isInt);
assertListOfInt(literal(0));
assertListOfInt(literal(1));
assertListOfInt(literal(2));
assertListOfInt(literal(3));
}
test_listLiteral_simple_disabled() async {
String code = r'''
void main () {
List<int> l0 = <num>[];
List<int> l1 = <num>[3];
List<int> l2 = <String>["hello"];
List<int> l3 = <dynamic>["hello", 3];
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
DartType literal(int i) {
VariableDeclarationStatement stmt = statements[i];
VariableDeclaration decl = stmt.variables.variables[0];
ListLiteral exp = decl.initializer;
return exp.staticType;
}
_isListOf(_isNum)(literal(0));
_isListOf(_isNum)(literal(1));
_isListOf(_isString)(literal(2));
_isListOf(_isDynamic)(literal(3));
}
test_listLiteral_simple_subtype() async {
String code = r'''
void main () {
Iterable<int> l0 = [];
Iterable<int> l1 = [3];
Iterable<int> l2 = ["hello"];
Iterable<int> l3 = ["hello", 3];
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
DartType literal(int i) {
VariableDeclarationStatement stmt = statements[i];
VariableDeclaration decl = stmt.variables.variables[0];
ListLiteral exp = decl.initializer;
return exp.staticType;
}
Asserter<InterfaceType> assertListOfInt = _isListOf(_isInt);
assertListOfInt(literal(0));
assertListOfInt(literal(1));
assertListOfInt(literal(2));
assertListOfInt(literal(3));
}
test_mapLiteral_nested() async {
String code = r'''
void main () {
Map<int, List<String>> l0 = {};
Map<int, List<String>> l1 = {3: ["hello"]};
Map<int, List<String>> l2 = {"hello": ["hello"]};
Map<int, List<String>> l3 = {3: [3]};
Map<int, List<String>> l4 = {3:["hello"], "hello": [3]};
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
MapLiteral literal(int i) {
VariableDeclarationStatement stmt = statements[i];
VariableDeclaration decl = stmt.variables.variables[0];
MapLiteral exp = decl.initializer;
return exp;
}
Asserter<InterfaceType> assertListOfString = _isListOf(_isString);
Asserter<InterfaceType> assertMapOfIntToListOfString =
_isMapOf(_isInt, (DartType type) => assertListOfString(type));
assertMapOfIntToListOfString(literal(0).staticType);
assertMapOfIntToListOfString(literal(1).staticType);
assertMapOfIntToListOfString(literal(2).staticType);
assertMapOfIntToListOfString(literal(3).staticType);
assertMapOfIntToListOfString(literal(4).staticType);
assertListOfString(literal(1).entries[0].value.staticType);
assertListOfString(literal(2).entries[0].value.staticType);
assertListOfString(literal(3).entries[0].value.staticType);
assertListOfString(literal(4).entries[0].value.staticType);
}
test_mapLiteral_simple() async {
String code = r'''
void main () {
Map<int, String> l0 = {};
Map<int, String> l1 = {3: "hello"};
Map<int, String> l2 = {"hello": "hello"};
Map<int, String> l3 = {3: 3};
Map<int, String> l4 = {3:"hello", "hello": 3};
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
DartType literal(int i) {
VariableDeclarationStatement stmt = statements[i];
VariableDeclaration decl = stmt.variables.variables[0];
MapLiteral exp = decl.initializer;
return exp.staticType;
}
Asserter<InterfaceType> assertMapOfIntToString =
_isMapOf(_isInt, _isString);
assertMapOfIntToString(literal(0));
assertMapOfIntToString(literal(1));
assertMapOfIntToString(literal(2));
assertMapOfIntToString(literal(3));
}
test_mapLiteral_simple_disabled() async {
String code = r'''
void main () {
Map<int, String> l0 = <int, dynamic>{};
Map<int, String> l1 = <int, dynamic>{3: "hello"};
Map<int, String> l2 = <int, dynamic>{"hello": "hello"};
Map<int, String> l3 = <int, dynamic>{3: 3};
}
''';
CompilationUnit unit = await resolveSource(code);
List<Statement> statements =
AstFinder.getStatementsInTopLevelFunction(unit, "main");
DartType literal(int i) {
VariableDeclarationStatement stmt = statements[i];
VariableDeclaration decl = stmt.variables.variables[0];
MapLiteral exp = decl.initializer;
return exp.staticType;
}
Asserter<InterfaceType> assertMapOfIntToDynamic =
_isMapOf(_isInt, _isDynamic);
assertMapOfIntToDynamic(literal(0));
assertMapOfIntToDynamic(literal(1));
assertMapOfIntToDynamic(literal(2));
assertMapOfIntToDynamic(literal(3));
}
test_methodDeclaration_body_propagation() async {
String code = r'''
class A {
List<String> m0(int x) => ["hello"];
List<String> m1(int x) {return [3];}
}
''';
CompilationUnit unit = await resolveSource(code);
Expression methodReturnValue(String methodName) {
MethodDeclaration method =
AstFinder.getMethodInClass(unit, "A", methodName);
FunctionBody body = method.body;
if (body is ExpressionFunctionBody) {
return body.expression;
} else {
Statement stmt = (body as BlockFunctionBody).block.statements[0];
return (stmt as ReturnStatement).expression;
}
}
Asserter<InterfaceType> assertListOfString = _isListOf(_isString);
assertListOfString(methodReturnValue("m0").staticType);
assertListOfString(methodReturnValue("m1").staticType);
}
test_partialTypes1() async {
// Test that downwards inference with a partial type
// correctly uses the partial information to fill in subterm
// types
String code = r'''
typedef To Func1<From, To>(From x);
S f<S, T>(Func1<S, T> g) => null;
String test() => f((l) => l.length);
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
ExpressionFunctionBody body = test.functionExpression.body;
_isString(body.expression.staticType);
MethodInvocation invoke = body.expression;
FunctionExpression function = invoke.argumentList.arguments[0];
ExecutableElement f0 = function.element;
FunctionType type = f0.type;
_isFunction2Of(_isString, _isInt)(type);
}
test_pinning_multipleConstraints1() async {
// Test that downwards inference with two different downwards covariant
// constraints on the same parameter correctly fails to infer when
// the types do not share a common subtype
String code = r'''
class A<S, T> {
S s;
T t;
}
class B<S> extends A<S, S> { B(S s); }
A<int, String> test() => new B(3);
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertErrors(source, [StrongModeCode.INVALID_CAST_LITERAL]);
verify([source]);
CompilationUnit unit = analysisResult.unit;
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
ExpressionFunctionBody body = test.functionExpression.body;
DartType type = body.expression.staticType;
Element elementB = AstFinder.getClass(unit, "B").element;
_isInstantiationOf(_hasElement(elementB))([_isNull])(type);
}
test_pinning_multipleConstraints2() async {
// Test that downwards inference with two identical downwards covariant
// constraints on the same parameter correctly infers and pins the type
String code = r'''
class A<S, T> {
S s;
T t;
}
class B<S> extends A<S, S> { B(S s); }
A<num, num> test() => new B(3);
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
ExpressionFunctionBody body = test.functionExpression.body;
DartType type = body.expression.staticType;
Element elementB = AstFinder.getClass(unit, "B").element;
_isInstantiationOf(_hasElement(elementB))([_isNum])(type);
}
test_pinning_multipleConstraints3() async {
// Test that downwards inference with two different downwards covariant
// constraints on the same parameter correctly fails to infer when
// the types do not share a common subtype, but do share a common supertype
String code = r'''
class A<S, T> {
S s;
T t;
}
class B<S> extends A<S, S> { B(S s); }
A<int, double> test() => new B(3);
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertErrors(source, [
StrongModeCode.INVALID_CAST_LITERAL,
]);
verify([source]);
CompilationUnit unit = analysisResult.unit;
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
ExpressionFunctionBody body = test.functionExpression.body;
DartType type = body.expression.staticType;
Element elementB = AstFinder.getClass(unit, "B").element;
_isInstantiationOf(_hasElement(elementB))([_isNull])(type);
}
test_pinning_multipleConstraints4() async {
// Test that downwards inference with two subtype related downwards
// covariant constraints on the same parameter correctly infers and pins
// the type
String code = r'''
class A<S, T> {
S s;
T t;
}
class B<S> extends A<S, S> {}
A<int, num> test() => new B();
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
ExpressionFunctionBody body = test.functionExpression.body;
DartType type = body.expression.staticType;
Element elementB = AstFinder.getClass(unit, "B").element;
_isInstantiationOf(_hasElement(elementB))([_isInt])(type);
}
test_pinning_multipleConstraints_contravariant1() async {
// Test that downwards inference with two different downwards contravariant
// constraints on the same parameter chooses the upper bound
// when the only supertype is Object
String code = r'''
class A<S, T> {
S s;
T t;
}
class B<S> extends A<S, S> {}
typedef void Contra1<T>(T x);
Contra1<A<S, S>> mkA<S>() => (A<S, S> x) {};
Contra1<A<int, String>> test() => mkA();
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
ExpressionFunctionBody body = test.functionExpression.body;
FunctionType functionType = body.expression.staticType;
DartType type = functionType.normalParameterTypes[0];
Element elementA = AstFinder.getClass(unit, "A").element;
_isInstantiationOf(_hasElement(elementA))([_isObject, _isObject])(type);
}
test_pinning_multipleConstraints_contravariant2() async {
// Test that downwards inference with two identical downwards contravariant
// constraints on the same parameter correctly pins the type
String code = r'''
class A<S, T> {
S s;
T t;
}
class B<S> extends A<S, S> {}
typedef void Contra1<T>(T x);
Contra1<A<S, S>> mkA<S>() => (A<S, S> x) {};
Contra1<A<num, num>> test() => mkA();
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
ExpressionFunctionBody body = test.functionExpression.body;
FunctionType functionType = body.expression.staticType;
DartType type = functionType.normalParameterTypes[0];
Element elementA = AstFinder.getClass(unit, "A").element;
_isInstantiationOf(_hasElement(elementA))([_isNum, _isNum])(type);
}
test_pinning_multipleConstraints_contravariant3() async {
// Test that downwards inference with two different downwards contravariant
// constraints on the same parameter correctly choose the least upper bound
// when they share a common supertype
String code = r'''
class A<S, T> {
S s;
T t;
}
class B<S> extends A<S, S> {}
typedef void Contra1<T>(T x);
Contra1<A<S, S>> mkA<S>() => (A<S, S> x) {};
Contra1<A<int, double>> test() => mkA();
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
ExpressionFunctionBody body = test.functionExpression.body;
FunctionType functionType = body.expression.staticType;
DartType type = functionType.normalParameterTypes[0];
Element elementA = AstFinder.getClass(unit, "A").element;
_isInstantiationOf(_hasElement(elementA))([_isNum, _isNum])(type);
}
test_pinning_multipleConstraints_contravariant4() async {
// Test that downwards inference with two different downwards contravariant
// constraints on the same parameter correctly choose the least upper bound
// when one is a subtype of the other
String code = r'''
class A<S, T> {
S s;
T t;
}
class B<S> extends A<S, S> {}
typedef void Contra1<T>(T x);
Contra1<A<S, S>> mkA<S>() => (A<S, S> x) {};
Contra1<A<int, num>> test() => mkA();
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
ExpressionFunctionBody body = test.functionExpression.body;
FunctionType functionType = body.expression.staticType;
DartType type = functionType.normalParameterTypes[0];
Element elementA = AstFinder.getClass(unit, "A").element;
_isInstantiationOf(_hasElement(elementA))([_isNum, _isNum])(type);
}
test_redirectedConstructor_named() async {
Source source = addSource(r'''
class A<T, U> implements B<T, U> {
A.named();
}
class B<T2, U2> {
factory B() = A.named;
}
''');
TestAnalysisResult result = await computeAnalysisResult(source);
assertNoErrors(source);
ClassDeclaration b = result.unit.declarations[1];
ConstructorDeclaration bConstructor = b.members[0];
ConstructorName redirected = bConstructor.redirectedConstructor;
TypeName typeName = redirected.type;
expect(typeName.type.toString(), 'A<T2, U2>');
expect(typeName.type.toString(), 'A<T2, U2>');
var constructorMember = redirected.staticElement;
expect(constructorMember.toString(), 'A.named() → A<T2, U2>');
expect(redirected.name.staticElement, constructorMember);
}
test_redirectedConstructor_self() async {
Source source = addSource(r'''
class A<T> {
A();
factory A.redirected() = A;
}
''');
await computeAnalysisResult(source);
assertNoErrors(source);
}
test_redirectedConstructor_unnamed() async {
Source source = addSource(r'''
class A<T, U> implements B<T, U> {
A();
}
class B<T2, U2> {
factory B() = A;
}
''');
TestAnalysisResult result = await computeAnalysisResult(source);
assertNoErrors(source);
ClassDeclaration b = result.unit.declarations[1];
ConstructorDeclaration bConstructor = b.members[0];
ConstructorName redirected = bConstructor.redirectedConstructor;
TypeName typeName = redirected.type;
expect(typeName.type.toString(), 'A<T2, U2>');
expect(typeName.type.toString(), 'A<T2, U2>');
expect(redirected.name, isNull);
expect(redirected.staticElement.toString(), 'A() → A<T2, U2>');
}
test_redirectingConstructor_propagation() async {
String code = r'''
class A {
A() : this.named([]);
A.named(List<String> x);
}
''';
CompilationUnit unit = await resolveSource(code);
ConstructorDeclaration constructor =
AstFinder.getConstructorInClass(unit, "A", null);
RedirectingConstructorInvocation invocation = constructor.initializers[0];
Expression exp = invocation.argumentList.arguments[0];
_isListOf(_isString)(exp.staticType);
}
test_returnType_variance1() async {
// Check that downwards inference correctly pins a type parameter
// when the parameter is constrained in a contravariant position
String code = r'''
typedef To Func1<From, To>(From x);
Func1<T, String> f<T>(T x) => null;
Func1<num, String> test() => f(42);
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
ExpressionFunctionBody body = test.functionExpression.body;
MethodInvocation invoke = body.expression;
_isFunction2Of(_isNum, _isFunction2Of(_isNum, _isString))(
invoke.staticInvokeType);
}
test_returnType_variance2() async {
// Check that downwards inference correctly pins a type parameter
// when the parameter is constrained in a covariant position
String code = r'''
typedef To Func1<From, To>(From x);
Func1<String, T> f<T>(T x) => null;
Func1<String, num> test() => f(42);
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
ExpressionFunctionBody body = test.functionExpression.body;
MethodInvocation invoke = body.expression;
_isFunction2Of(_isNum, _isFunction2Of(_isString, _isNum))(
invoke.staticInvokeType);
}
test_returnType_variance3() async {
// Check that the variance heuristic chooses the most precise type
// when the return type uses the variable in a contravariant position
// and there is no downwards constraint.
String code = r'''
typedef To Func1<From, To>(From x);
Func1<T, String> f<T>(T x, g(T x)) => null;
dynamic test() => f(42, (num x) => x);
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
ExpressionFunctionBody body = test.functionExpression.body;
FunctionType functionType = body.expression.staticType;
DartType type = functionType.normalParameterTypes[0];
_isInt(type);
}
test_returnType_variance4() async {
// Check that the variance heuristic chooses the more precise type
// when the return type uses the variable in a covariant position
// and there is no downwards constraint
String code = r'''
typedef To Func1<From, To>(From x);
Func1<String, T> f<T>(T x, g(T x)) => null;
dynamic test() => f(42, (num x) => x);
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
ExpressionFunctionBody body = test.functionExpression.body;
FunctionType functionType = body.expression.staticType;
DartType type = functionType.returnType;
_isInt(type);
}
test_returnType_variance5() async {
// Check that pinning works correctly with a partial type
// when the return type uses the variable in a contravariant position
String code = r'''
typedef To Func1<From, To>(From x);
Func1<T, String> f<T>(T x) => null;
T g<T, S>(Func1<T, S> f) => null;
num test() => g(f(3));
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
ExpressionFunctionBody body = test.functionExpression.body;
MethodInvocation call = body.expression;
_isNum(call.staticType);
_isFunction2Of(_isFunction2Of(_isNum, _isString), _isNum)(
call.staticInvokeType);
}
test_returnType_variance6() async {
// Check that pinning works correctly with a partial type
// when the return type uses the variable in a covariant position
String code = r'''
typedef To Func1<From, To>(From x);
Func1<String, T> f<T>(T x) => null;
T g<T, S>(Func1<S, T> f) => null;
num test() => g(f(3));
''';
Source source = addSource(code);
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
CompilationUnit unit = analysisResult.unit;
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test");
ExpressionFunctionBody body = test.functionExpression.body;
MethodInvocation call = body.expression;
_isNum(call.staticType);
_isFunction2Of(_isFunction2Of(_isString, _isNum), _isNum)(
call.staticInvokeType);
}
test_superConstructorInvocation_propagation() async {
String code = r'''
class B {
B(List<String> p);
}
class A extends B {
A() : super([]);
}
''';
CompilationUnit unit = await resolveSource(code);
ConstructorDeclaration constructor =
AstFinder.getConstructorInClass(unit, "A", null);
SuperConstructorInvocation invocation = constructor.initializers[0];
Expression exp = invocation.argumentList.arguments[0];
_isListOf(_isString)(exp.staticType);
}
test_sync_star_method_propagation() async {
String code = r'''
import "dart:async";
class A {
Iterable f0() sync* { yield []; }
Iterable f1() sync* { yield* new List(); }
Iterable<List<int>> f2() sync* { yield []; }
Iterable<List<int>> f3() sync* { yield* new List(); }
}
''';
CompilationUnit unit = await resolveSource(code);
void check(String name, Asserter<InterfaceType> typeTest) {
MethodDeclaration test = AstFinder.getMethodInClass(unit, "A", name);
BlockFunctionBody body = test.body;
YieldStatement stmt = body.block.statements[0];
Expression exp = stmt.expression;
typeTest(exp.staticType);
}
check("f0", _isListOf(_isDynamic));
check("f1", _isListOf(_isDynamic));
check("f2", _isListOf(_isInt));
check("f3", _isListOf((DartType type) => _isListOf(_isInt)(type)));
}
test_sync_star_propagation() async {
String code = r'''
import "dart:async";
Iterable f0() sync* { yield []; }
Iterable f1() sync* { yield* new List(); }
Iterable<List<int>> f2() sync* { yield []; }
Iterable<List<int>> f3() sync* { yield* new List(); }
''';
CompilationUnit unit = await resolveSource(code);
void check(String name, Asserter<InterfaceType> typeTest) {
FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, name);
BlockFunctionBody body = test.functionExpression.body;
YieldStatement stmt = body.block.statements[0];
Expression exp = stmt.expression;
typeTest(exp.staticType);
}
check("f0", _isListOf(_isDynamic));
check("f1", _isListOf(_isDynamic));
check("f2", _isListOf(_isInt));
check("f3", _isListOf((DartType type) => _isListOf(_isInt)(type)));
}
/// Verifies the source has the expected [errorCodes] as well as the
/// expected [errorMessage].
void _expectInferenceError(
Source source, List<ErrorCode> errorCodes, String errorMessage) {
assertErrors(source, errorCodes);
var errors = analysisResults[source]
.errors
.where((e) => e.errorCode == StrongModeCode.COULD_NOT_INFER)
.map((e) => e.message)
.toList();
expect(errors.length, 1);
var actual = errors[0];
expect(actual,
errorMessage, // Print the literal error message for easy copy+paste:
reason: 'Actual error did not match expected error:\n$actual');
}
/// Helper method for testing `FutureOr<T>`.
///
/// Validates that [code] produces [errors]. It should define a function
/// "test", whose body is an expression that invokes a method. Returns that
/// invocation.
Future<MethodInvocation> _testFutureOr(String code,
{List<ErrorCode> errors}) async {
Source source = addSource("""
import "dart:async";
$code""");
TestAnalysisResult analysisResult = await computeAnalysisResult(source);
if (errors == null) {
assertNoErrors(source);
} else {
assertErrors(source, errors);
}
verify([source]);
FunctionDeclaration test =
AstFinder.getTopLevelFunction(analysisResult.unit, "test");
ExpressionFunctionBody body = test.functionExpression.body;
return body.expression;
}
}
/**
* Strong mode static analyzer end to end tests
*/
@reflectiveTest
class StrongModeStaticTypeAnalyzer2Test extends StaticTypeAnalyzer2TestShared {
void expectStaticInvokeType(String search, String type) {
var invocation = findIdentifier(search).parent as MethodInvocation;
expect(invocation.staticInvokeType.toString(), type);
}
void setUp() {
super.setUp();
AnalysisOptionsImpl options = new AnalysisOptionsImpl();
options.strongMode = true;
resetWith(options: options);
}
test_dynamicObjectGetter_hashCode() async {
String code = r'''
main() {
dynamic a = null;
var foo = a.hashCode;
}
''';
await resolveTestUnit(code);
expectInitializerType('foo', 'int', isNull);
}
test_dynamicObjectMethod_toString() async {
String code = r'''
main() {
dynamic a = null;
var foo = a.toString();
}
''';
await resolveTestUnit(code);
expectInitializerType('foo', 'String', isNull);
}
test_futureOr_promotion1() async {
// Test that promotion from FutureOr<T> to T works for concrete types
String code = r'''
import "dart:async";
dynamic test(FutureOr<int> x) => (x is int) && (x.abs() == 0);
''';
await resolveTestUnit(code);
}
test_futureOr_promotion2() async {
// Test that promotion from FutureOr<T> to Future<T> works for concrete
// types
String code = r'''
import "dart:async";
dynamic test(FutureOr<int> x) => (x is Future<int>) &&
(x.then((x) => x) == null);
''';
await resolveTestUnit(code);
}
test_futureOr_promotion3() async {
// Test that promotion from FutureOr<T> to T works for type
// parameters T
String code = r'''
import "dart:async";
dynamic test<T extends num>(FutureOr<T> x) => (x is T) &&
(x.abs() == 0);
''';
await resolveTestUnit(code);
}
test_futureOr_promotion4() async {
// Test that promotion from FutureOr<T> to Future<T> works for type
// parameters T
String code = r'''
import "dart:async";
dynamic test<T extends num>(FutureOr<T> x) => (x is Future<T>) &&
(x.then((x) => x) == null);
''';
await resolveTestUnit(code);
}
test_generalizedVoid_assignToVoidOk() async {
Source source = addSource(r'''
void main() {
void x;
x = 42;
}
''');
await computeAnalysisResult(source);
assertNoErrors(source);
}
test_genericFunction() async {
await resolveTestUnit(r'T f<T>(T x) => null;');
expectFunctionType('f', '<T>(T) → T',
elementTypeParams: '[T]', typeFormals: '[T]');
SimpleIdentifier f = findIdentifier('f');
FunctionElementImpl e = f.staticElement;
FunctionType ft = e.type.instantiate([typeProvider.stringType]);
expect(ft.toString(), '(String) → String');
}
test_genericFunction_bounds() async {
await resolveTestUnit(r'T f<T extends num>(T x) => null;');
expectFunctionType('f', '<T extends num>(T) → T',
elementTypeParams: '[T extends num]', typeFormals: '[T extends num]');
}
test_genericFunction_parameter() async {
await resolveTestUnit(r'''
void g(T f<T>(T x)) {}
''', noErrors: false // TODO(paulberry): remove when dartbug.com/28515 fixed.
);
expectFunctionType('f', '<T>(T) → T',
elementTypeParams: '[]', typeFormals: '[T]');
SimpleIdentifier f = findIdentifier('f');
ParameterElementImpl e = f.staticElement;
FunctionType type = e.type;
FunctionType ft = type.instantiate([typeProvider.stringType]);
expect(ft.toString(), '(String) → String');
}
test_genericFunction_static() async {
await resolveTestUnit(r'''
class C<E> {
static T f<T>(T x) => null;
}
''');
expectFunctionType('f', '<T>(T) → T',
elementTypeParams: '[T]', typeFormals: '[T]');
SimpleIdentifier f = findIdentifier('f');
MethodElementImpl e = f.staticElement;
FunctionType ft = e.type.instantiate([typeProvider.stringType]);
expect(ft.toString(), '(String) → String');
}
test_genericFunction_typedef() async {
String code = r'''
typedef T F<T>(T x);
F f0;
class C {
static F f1;
F f2;
void g(F f3) {
F f4;
f0(3);
f1(3);
f2(3);
f3(3);
f4(3);
}
}
class D<S> {
static F f1;
F f2;
void g(F f3) {
F f4;
f0(3);
f1(3);
f2(3);
f3(3);
f4(3);
}
}
''';
await resolveTestUnit(code);
checkBody(String className) {
List<Statement> statements =
AstFinder.getStatementsInMethod(testUnit, className, "g");
for (int i = 1; i <= 5; i++) {
Expression exp = (statements[i] as ExpressionStatement).expression;
expect(exp.staticType, typeProvider.dynamicType);
}
}
checkBody("C");
checkBody("D");
}
test_genericFunction_upwardsAndDownwards() async {
// Regression tests for https://github.com/dart-lang/sdk/issues/27586.
await resolveTestUnit(r'List<num> x = [1, 2];');
expectInitializerType('x', 'List<num>');
}
test_genericFunction_upwardsAndDownwards_Object() async {
// Regression tests for https://github.com/dart-lang/sdk/issues/27625.
await resolveTestUnit(r'''
List<Object> aaa = [];
List<Object> bbb = [1, 2, 3];
List<Object> ccc = [null];
List<Object> ddd = [1 as dynamic];
List<Object> eee = [new Object()];
''');
expectInitializerType('aaa', 'List<Object>');
expectInitializerType('bbb', 'List<Object>');
expectInitializerType('ccc', 'List<Object>');
expectInitializerType('ddd', 'List<Object>');
expectInitializerType('eee', 'List<Object>');
}
test_genericMethod() async {
await resolveTestUnit(r'''
class C<E> {
List<T> f<T>(E e) => null;
}
main() {
C<String> cOfString;
}
''');
expectFunctionType('f', '<T>(E) → List<T>',
elementTypeParams: '[T]',
typeParams: '[E]',
typeArgs: '[E]',
typeFormals: '[T]');
SimpleIdentifier c = findIdentifier('cOfString');
FunctionType ft = (c.staticType as InterfaceType).getMethod('f').type;
expect(ft.toString(), '<T>(String) → List<T>');
ft = ft.instantiate([typeProvider.intType]);
expect(ft.toString(), '(String) → List<int>');
expect('${ft.typeArguments}/${ft.typeParameters}', '[String, int]/[E, T]');
}
test_genericMethod_explicitTypeParams() async {
await resolveTestUnit(r'''
class C<E> {
List<T> f<T>(E e) => null;
}
main() {
C<String> cOfString;
var x = cOfString.f<int>('hi');
}
''');
MethodInvocation f = findIdentifier('f<int>').parent;
FunctionType ft = f.staticInvokeType;
expect(ft.toString(), '(String) → List<int>');
expect('${ft.typeArguments}/${ft.typeParameters}', '[String, int]/[E, T]');
SimpleIdentifier x = findIdentifier('x');
expect(x.staticType,
typeProvider.listType.instantiate([typeProvider.intType]));
}
test_genericMethod_functionExpressionInvocation_explicit() async {
await resolveTestUnit(r'''
class C<E> {
T f<T>(T e) => null;
static T g<T>(T e) => null;
static T Function<T>(T) h = null;
}
T topF<T>(T e) => null;
var topG = topF;
void test<S>(T Function<T>(T) pf) {
var c = new C<int>();
T lf<T>(T e) => null;
var lambdaCall = (<E>(E e) => e)<int>(3);
var methodCall = (c.f)<int>(3);
var staticCall = (C.g)<int>(3);
var staticFieldCall = (C.h)<int>(3);
var topFunCall = (topF)<int>(3);
var topFieldCall = (topG)<int>(3);
var localCall = (lf)<int>(3);
var paramCall = (pf)<int>(3);
}
''');
expectIdentifierType('methodCall', "int");
expectIdentifierType('staticCall', "int");
expectIdentifierType('staticFieldCall', "int");
expectIdentifierType('topFunCall', "int");
expectIdentifierType('topFieldCall', "int");
expectIdentifierType('localCall', "int");
expectIdentifierType('paramCall', "int");
expectIdentifierType('lambdaCall', "int");
}
test_genericMethod_functionExpressionInvocation_functionTypedParameter_explicit() async {
await resolveTestUnit(r'''
void test<S>(T pf<T>(T e)) {
var paramCall = (pf)<int>(3);
}
''', noErrors: false // TODO(paulberry): remove when dartbug.com/28515 fixed.
);
expectIdentifierType('paramCall', "int");
}
test_genericMethod_functionExpressionInvocation_functionTypedParameter_inferred() async {
await resolveTestUnit(r'''
void test<S>(T pf<T>(T e)) {
var paramCall = (pf)(3);
}
''', noErrors: false // TODO(paulberry): remove when dartbug.com/28515 fixed.
);
expectIdentifierType('paramCall', "int");
}
test_genericMethod_functionExpressionInvocation_inferred() async {
await resolveTestUnit(r'''
class C<E> {
T f<T>(T e) => null;
static T g<T>(T e) => null;
static T Function<T>(T) h = null;
}
T topF<T>(T e) => null;
var topG = topF;
void test<S>(T Function<T>(T) pf) {
var c = new C<int>();
T lf<T>(T e) => null;
var lambdaCall = (<E>(E e) => e)(3);
var methodCall = (c.f)(3);
var staticCall = (C.g)(3);
var staticFieldCall = (C.h)(3);
var topFunCall = (topF)(3);
var topFieldCall = (topG)(3);
var localCall = (lf)(3);
var paramCall = (pf)(3);
}
''', noErrors: false // TODO(paulberry): remove when dartbug.com/28515 fixed.
);
expectIdentifierType('methodCall', "int");
expectIdentifierType('staticCall', "int");
expectIdentifierType('staticFieldCall', "int");
expectIdentifierType('topFunCall', "int");
expectIdentifierType('topFieldCall', "int");
expectIdentifierType('localCall', "int");
expectIdentifierType('paramCall', "int");
expectIdentifierType('lambdaCall', "int");
}
test_genericMethod_functionInvocation_explicit() async {
await resolveTestUnit(r'''
class C<E> {
T f<T>(T e) => null;
static T g<T>(T e) => null;
static T Function<T>(T) h = null;
}
T topF<T>(T e) => null;
var topG = topF;
void test<S>(T Function<T>(T) pf) {
var c = new C<int>();
T lf<T>(T e) => null;
var methodCall = c.f<int>(3);
var staticCall = C.g<int>(3);
var staticFieldCall = C.h<int>(3);
var topFunCall = topF<int>(3);
var topFieldCall = topG<int>(3);
var localCall = lf<int>(3);
var paramCall = pf<int>(3);
}
''');
expectIdentifierType('methodCall', "int");
expectIdentifierType('staticCall', "int");
expectIdentifierType('staticFieldCall', "int");
expectIdentifierType('topFunCall', "int");
expectIdentifierType('topFieldCall', "int");
expectIdentifierType('localCall', "int");
expectIdentifierType('paramCall', "int");
}
test_genericMethod_functionInvocation_functionTypedParameter_explicit() async {
await resolveTestUnit(r'''
void test<S>(T pf<T>(T e)) {
var paramCall = pf<int>(3);
}
''', noErrors: false // TODO(paulberry): remove when dartbug.com/28515 fixed.
);
expectIdentifierType('paramCall', "int");
}
test_genericMethod_functionInvocation_functionTypedParameter_inferred() async {
await resolveTestUnit(r'''
void test<S>(T pf<T>(T e)) {
var paramCall = pf(3);
}
''', noErrors: false // TODO(paulberry): remove when dartbug.com/28515 fixed.
);
expectIdentifierType('paramCall', "int");
}
test_genericMethod_functionInvocation_inferred() async {
await resolveTestUnit(r'''
class C<E> {
T f<T>(T e) => null;
static T g<T>(T e) => null;
static T Function<T>(T) h = null;
}
T topF<T>(T e) => null;
var topG = topF;
void test<S>(T Function<T>(T) pf) {
var c = new C<int>();
T lf<T>(T e) => null;
var methodCall = c.f(3);
var staticCall = C.g(3);
var staticFieldCall = C.h(3);
var topFunCall = topF(3);
var topFieldCall = topG(3);
var localCall = lf(3);
var paramCall = pf(3);
}
''');
expectIdentifierType('methodCall', "int");
expectIdentifierType('staticCall', "int");
expectIdentifierType('staticFieldCall', "int");
expectIdentifierType('topFunCall', "int");
expectIdentifierType('topFieldCall', "int");
expectIdentifierType('localCall', "int");
expectIdentifierType('paramCall', "int");
}
test_genericMethod_functionTypedParameter() async {
await resolveTestUnit(r'''
class C<E> {
List<T> f<T>(T f(E e)) => null;
}
main() {
C<String> cOfString;
}
''');
expectFunctionType('f', '<T>((E) → T) → List<T>',
elementTypeParams: '[T]',
typeParams: '[E]',
typeArgs: '[E]',
typeFormals: '[T]');
SimpleIdentifier c = findIdentifier('cOfString');
FunctionType ft = (c.staticType as InterfaceType).getMethod('f').type;