| // 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 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/nullability_suffix.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/src/dart/ast/extensions.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/error/codes.dart'; |
| import 'package:test/test.dart'; |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| |
| import '../src/dart/resolution/context_collection_resolution.dart'; |
| import '../utils.dart'; |
| import 'resolver_test_case.dart'; |
| import 'test_support.dart'; |
| |
| main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(StrongModeLocalInferenceTest); |
| defineReflectiveTests(StrongModeStaticTypeAnalyzer2Test); |
| defineReflectiveTests(StrongModeTypePropagationTest); |
| }); |
| } |
| |
| /// Strong mode static analyzer local type inference tests |
| @reflectiveTest |
| class StrongModeLocalInferenceTest extends PubPackageResolutionTest |
| with WithoutNullSafetyMixin { |
| // TODO(https://github.com/dart-lang/sdk/issues/44666): Use null safety in |
| // test cases. |
| TypeAssertions? _assertions; |
| |
| late final Asserter<DartType> _isDynamic; |
| late final Asserter<InterfaceType> _isFutureOfDynamic; |
| late final Asserter<InterfaceType> _isFutureOfInt; |
| late final Asserter<InterfaceType> _isFutureOfNull; |
| late final Asserter<InterfaceType> _isFutureOrOfInt; |
| late final Asserter<DartType> _isInt; |
| late final Asserter<DartType> _isNull; |
| late final Asserter<DartType> _isNum; |
| late final Asserter<DartType> _isObject; |
| late final Asserter<DartType> _isString; |
| |
| late final AsserterBuilder2<Asserter<DartType>, Asserter<DartType>, DartType> |
| _isFunction2Of; |
| late final AsserterBuilder<List<Asserter<DartType>>, InterfaceType> |
| _isFutureOf; |
| late final AsserterBuilder<List<Asserter<DartType>>, InterfaceType> |
| _isFutureOrOf; |
| late final AsserterBuilderBuilder<Asserter<DartType>, |
| List<Asserter<DartType>>, DartType> _isInstantiationOf; |
| late final AsserterBuilder<Asserter<DartType>, InterfaceType> _isListOf; |
| late final AsserterBuilder2<Asserter<DartType>, Asserter<DartType>, |
| InterfaceType> _isMapOf; |
| late final AsserterBuilder<DartType, DartType> _isType; |
| |
| late final AsserterBuilder<Element, DartType> _hasElement; |
| |
| CompilationUnit get unit => result.unit; |
| |
| @override |
| Future<void> resolveTestFile() async { |
| var result = await super.resolveTestFile(); |
| |
| var assertions = _assertions; |
| if (assertions == null) { |
| assertions = _assertions = 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; |
| _isFutureOf = _isInstantiationOf(_hasElement(typeProvider.futureElement)); |
| _isFutureOrOf = |
| _isInstantiationOf(_hasElement(typeProvider.futureOrElement)); |
| _isFutureOfDynamic = _isFutureOf([_isDynamic]); |
| _isFutureOfInt = _isFutureOf([_isInt]); |
| _isFutureOfNull = _isFutureOf([_isNull]); |
| _isFutureOrOfInt = _isFutureOrOf([_isInt]); |
| } |
| |
| return result; |
| } |
| |
| test_async_method_propagation() async { |
| String code = r''' |
| 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); } |
| } |
| '''; |
| await resolveTestCode(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 { |
| var stmt = |
| (body as BlockFunctionBody).block.statements[0] as ReturnStatement; |
| returnExp = stmt.expression!; |
| } |
| DartType type = returnExp.typeOrThrow; |
| if (returnExp is AwaitExpression) { |
| type = returnExp.expression.typeOrThrow; |
| } |
| typeTest(type as InterfaceType); |
| } |
| |
| 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''' |
| 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); } |
| '''; |
| await resolveTestCode(code); |
| |
| void check(String name, Asserter<InterfaceType> typeTest) { |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, name); |
| var body = test.functionExpression.body; |
| Expression returnExp; |
| if (body is ExpressionFunctionBody) { |
| returnExp = body.expression; |
| } else { |
| var stmt = |
| (body as BlockFunctionBody).block.statements[0] as ReturnStatement; |
| returnExp = stmt.expression!; |
| } |
| DartType type = returnExp.typeOrThrow; |
| if (returnExp is AwaitExpression) { |
| type = returnExp.expression.typeOrThrow; |
| } |
| typeTest(type as InterfaceType); |
| } |
| |
| 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_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]); |
| } |
| '''; |
| await resolveTestCode(code); |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| CascadeExpression fetch(int i) { |
| var stmt = statements[i] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| var exp = decl.initializer as CascadeExpression; |
| return exp; |
| } |
| |
| Element elementA = AstFinder.getClass(unit, "A").declaredElement!; |
| |
| CascadeExpression cascade = fetch(0); |
| _isInstantiationOf(_hasElement(elementA))([_isInt])(cascade.typeOrThrow); |
| var invoke = cascade.cascadeSections[0] as MethodInvocation; |
| var function = invoke.argumentList.arguments[1] as FunctionExpression; |
| ExecutableElement f0 = function.declaredElement!; |
| _isListOf(_isInt)(f0.type.returnType as InterfaceType); |
| 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); } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 60, 1), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "test"); |
| var stmt = statements[0] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| Expression call = decl.initializer!; |
| _isInt(call.typeOrThrow); |
| } |
| |
| 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); } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 60, 1), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "test"); |
| var stmt = statements[0] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| Expression call = decl.initializer!; |
| _isInt(call.typeOrThrow); |
| } |
| |
| test_constrainedByBounds3() async { |
| var code = r''' |
| T f<T extends S, S extends int>(S x) => null; |
| void test() { var x = f(3); } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 76, 1), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "test"); |
| var stmt = statements[0] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| Expression call = decl.initializer!; |
| _isInt(call.typeOrThrow); |
| } |
| |
| 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); } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 110, 1), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "test"); |
| var stmt = statements[0] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| Expression call = decl.initializer!; |
| _isInt(call.typeOrThrow); |
| } |
| |
| test_constrainedByBounds5() async { |
| // Test that upwards inference with two type variables does not |
| // propagate 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); } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 110, 1), |
| error(CompileTimeErrorCode.COULD_NOT_INFER, 114, 1), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "test"); |
| var stmt = statements[0] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| Expression call = decl.initializer!; |
| _isDynamic(call.typeOrThrow); |
| } |
| |
| test_constructorInitializer_propagation() async { |
| String code = r''' |
| class A { |
| List<String> x; |
| A() : this.x = []; |
| } |
| '''; |
| await assertNoErrorsInCode(code); |
| ConstructorDeclaration constructor = |
| AstFinder.getConstructorInClass(unit, "A", null); |
| var assignment = constructor.initializers[0] as ConstructorFieldInitializer; |
| Expression exp = assignment.expression; |
| _isListOf(_isString)(exp.staticType as InterfaceType); |
| } |
| |
| test_factoryConstructor_propagation() async { |
| String code = r''' |
| class A<T> { |
| factory A() { return new B(); } |
| } |
| class B<S> extends A<S> {} |
| '''; |
| await assertErrorsInCode(code, [ |
| error( |
| CompileTimeErrorCode.NO_GENERATIVE_CONSTRUCTORS_IN_SUPERCLASS, 92, 4), |
| ]); |
| |
| ConstructorDeclaration constructor = |
| AstFinder.getConstructorInClass(unit, "A", null); |
| var body = constructor.body as BlockFunctionBody; |
| var stmt = body.block.statements[0] as ReturnStatement; |
| var exp = stmt.expression as InstanceCreationExpression; |
| ClassElement elementB = AstFinder.getClass(unit, "B").declaredElement!; |
| ClassElement elementA = AstFinder.getClass(unit, "A").declaredElement!; |
| expect(exp.constructorName.type.typeOrThrow.element, elementB); |
| _isInstantiationOf(_hasElement(elementB))([ |
| _isType(elementA.typeParameters[0] |
| .instantiate(nullabilitySuffix: NullabilitySuffix.star)) |
| ])(exp.typeOrThrow); |
| } |
| |
| test_fieldDeclaration_propagation() async { |
| String code = r''' |
| class A { |
| List<String> f0 = ["hello"]; |
| } |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| VariableDeclaration field = AstFinder.getFieldInClass(unit, "A", "f0"); |
| |
| _isListOf(_isString)(field.initializer!.staticType as InterfaceType); |
| } |
| |
| 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; |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_ELEMENT, 144, 5), |
| ]); |
| |
| Asserter<InterfaceType> assertListOfInt = _isListOf(_isInt); |
| |
| FunctionDeclaration test1 = AstFinder.getTopLevelFunction(unit, "test1"); |
| var body = test1.functionExpression.body as ExpressionFunctionBody; |
| assertListOfInt(body.expression.staticType as InterfaceType); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "test2"); |
| |
| FunctionDeclaration inner = |
| (statements[0] as FunctionDeclarationStatement).functionDeclaration; |
| var body0 = inner.functionExpression.body as BlockFunctionBody; |
| var return0 = body0.block.statements[0] as ReturnStatement; |
| Expression anon0 = return0.expression!; |
| var type0 = anon0.staticType as FunctionType; |
| expect(type0.returnType, typeProvider.intType); |
| expect(type0.normalParameterTypes[0], typeProvider.stringType); |
| |
| var anon1 = |
| (statements[1] as ReturnStatement).expression as FunctionExpression; |
| FunctionType type1 = anon1.declaredElement!.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;}; |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 91, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 144, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 200, 2), |
| error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 205, 21), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 259, 2), |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 275, 1), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 309, 2), |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 330, 1), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| DartType literal(int i) { |
| var stmt = statements[i] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| var exp = decl.initializer as FunctionExpression; |
| return exp.declaredElement!.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;}; |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 91, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 140, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 192, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 244, 2), |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 256, 1), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 290, 2), |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 307, 1), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| DartType literal(int i) { |
| var stmt = statements[i] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| var exp = decl.initializer as FunctionExpression; |
| return exp.declaredElement!.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];}; |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 97, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 161, 2), |
| error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 166, 23), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 228, 2), |
| error(CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE, 245, 1), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 286, 2), |
| error(CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE, 308, 1), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| Expression functionReturnValue(int i) { |
| var stmt = statements[i] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| var exp = decl.initializer as FunctionExpression; |
| 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 as InterfaceType); |
| assertListOfString(functionReturnValue(1).staticType as InterfaceType); |
| assertListOfString(functionReturnValue(2).staticType as InterfaceType); |
| assertListOfString(functionReturnValue(3).staticType as InterfaceType); |
| } |
| |
| 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;}); |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 262, 21), |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 337, 1), |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 397, 1), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| DartType literal(int i) { |
| var stmt = statements[i] as ExpressionStatement; |
| var invk = stmt.expression as FunctionExpressionInvocation; |
| var exp = invk.argumentList.arguments[0] as FunctionExpression; |
| return exp.declaredElement!.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;}); |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 318, 1), |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 374, 1), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| DartType literal(int i) { |
| var stmt = statements[i] as ExpressionStatement; |
| var invk = stmt.expression as FunctionExpressionInvocation; |
| var exp = invk.argumentList.arguments[0] as FunctionExpression; |
| return exp.declaredElement!.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;}); |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 153, 21), |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 200, 1), |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 232, 1), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| DartType literal(int i) { |
| var stmt = statements[i] as ExpressionStatement; |
| var invk = stmt.expression as MethodInvocation; |
| var exp = invk.argumentList.arguments[0] as FunctionExpression; |
| return exp.declaredElement!.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;}); |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 181, 1), |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 209, 1), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| DartType literal(int i) { |
| var stmt = statements[i] as ExpressionStatement; |
| var invk = stmt.expression as MethodInvocation; |
| var exp = invk.argumentList.arguments[0] as FunctionExpression; |
| return exp.declaredElement!.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;}); |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 256, 21), |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 329, 1), |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 387, 1), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| DartType literal(int i) { |
| var stmt = statements[i] as ExpressionStatement; |
| var invk = stmt.expression as MethodInvocation; |
| var exp = invk.argumentList.arguments[0] as FunctionExpression; |
| return exp.declaredElement!.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;}); |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 310, 1), |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 364, 1), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| DartType literal(int i) { |
| var stmt = statements[i] as ExpressionStatement; |
| var invk = stmt.expression as MethodInvocation; |
| var exp = invk.argumentList.arguments[0] as FunctionExpression; |
| return exp.declaredElement!.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(); |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 88, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 131, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 179, 2), |
| error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 191, 1), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 225, 2), |
| error(CompileTimeErrorCode.UNDEFINED_METHOD, 239, 11), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 288, 2), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| Expression functionReturnValue(int i) { |
| var stmt = statements[i] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| var exp = decl.initializer as FunctionExpression; |
| 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 as InterfaceType); |
| } |
| |
| 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 as InterfaceType); |
| } |
| |
| 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 as InterfaceType); |
| } |
| |
| 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 as InterfaceType); |
| } |
| |
| 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 as InterfaceType); |
| } |
| |
| 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 as InterfaceType); |
| } |
| |
| 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 as InterfaceType); |
| } |
| |
| 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 as InterfaceType); |
| _isFutureOfInt( |
| invoke.argumentList.arguments[0].staticType as InterfaceType); |
| } |
| |
| 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 as InterfaceType); |
| _isFutureOfInt( |
| invoke.argumentList.arguments[0].staticType as InterfaceType); |
| } |
| |
| 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 as InterfaceType); |
| _isFutureOf([_isNum])( |
| invoke.argumentList.arguments[0].staticType as InterfaceType); |
| } |
| |
| 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 as InterfaceType); |
| _isFutureOfInt( |
| invoke.argumentList.arguments[0].staticType as InterfaceType); |
| } |
| |
| 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 as InterfaceType); |
| _isFutureOfInt( |
| invoke.argumentList.arguments[0].staticType as InterfaceType); |
| } |
| |
| 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 as InterfaceType); |
| _isFutureOfInt( |
| invoke.argumentList.arguments[0].staticType as InterfaceType); |
| } |
| |
| 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 as InterfaceType); |
| _isInt(invoke.argumentList.arguments[0].typeOrThrow); |
| } |
| |
| 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.typeOrThrow); |
| } |
| |
| 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(); |
| ''', expectedErrors: [ |
| error(CompileTimeErrorCode.UNDEFINED_METHOD, 61, 3), |
| ]); |
| _isDynamic(invoke.typeOrThrow); |
| } |
| |
| 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); |
| ''', expectedErrors: [ |
| error(CompileTimeErrorCode.UNDEFINED_METHOD, 61, 4), |
| ]); |
| _isDynamic(invoke.typeOrThrow); |
| } |
| |
| 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(); |
| ''', expectedErrors: [ |
| error(CompileTimeErrorCode.UNDEFINED_METHOD, 65, 3), |
| ]); |
| _isDynamic(invoke.typeOrThrow); |
| } |
| |
| 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].typeOrThrow); |
| _isFutureOfNull(invoke.staticType as InterfaceType); |
| } |
| |
| 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].typeOrThrow); |
| _isFutureOfNull(invoke.staticType as InterfaceType); |
| } |
| |
| 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].typeOrThrow); |
| _isFutureOfNull(invoke.staticType as InterfaceType); |
| } |
| |
| 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 as InterfaceType); |
| } |
| |
| 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)); |
| ''', expectedErrors: [ |
| error(CompileTimeErrorCode.COULD_NOT_INFER, 111, 2), |
| ]); |
| _isFutureOfInt(invoke.staticType as InterfaceType); |
| } |
| |
| 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].typeOrThrow); |
| _isFutureOfNull(invoke.staticType as InterfaceType); |
| } |
| |
| 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].typeOrThrow); |
| _isFutureOfNull(invoke.staticType as InterfaceType); |
| } |
| |
| 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].typeOrThrow); |
| _isFutureOfNull(invoke.staticType as InterfaceType); |
| } |
| |
| 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)); |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 205, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 241, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 284, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 318, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 347, 2), |
| ]); |
| |
| Element elementA = AstFinder.getClass(unit, "A").declaredElement!; |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "test"); |
| void check(int i) { |
| var stmt = statements[i] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| Expression init = decl.initializer!; |
| _isInstantiationOf(_hasElement(elementA))([_isInt])(init.typeOrThrow); |
| } |
| |
| for (var i = 0; i < 5; i++) { |
| check(i); |
| } |
| } |
| |
| test_inferConstructor_unknownTypeLowerBound() async { |
| var code = 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) {}); |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 225, 1), |
| ]); |
| |
| DartType cType = findElement.localVar('c').type; |
| Element elementC = AstFinder.getClass(unit, "C").declaredElement!; |
| |
| _isInstantiationOf(_hasElement(elementC))([_isDynamic])(cType); |
| } |
| |
| test_inference_error_arguments() async { |
| var code = 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 assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 84, 1), |
| error(CompileTimeErrorCode.COULD_NOT_INFER, 88, 1), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 90, 15), |
| ]); |
| _expectInferenceError(r''' |
| Couldn't infer type parameter 'T'. |
| |
| Tried to infer 'double' for 'T' which doesn't work: |
| Parameter 'f' declared as 'T Function(T)' |
| but argument is 'double Function(int)'. |
| |
| Consider passing explicit type argument(s) to the generic. |
| |
| '''); |
| } |
| |
| test_inference_error_arguments2() async { |
| var code = 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 assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 95, 1), |
| error(CompileTimeErrorCode.COULD_NOT_INFER, 99, 1), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 101, 15), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 118, 16), |
| ]); |
| _expectInferenceError(r''' |
| Couldn't infer type parameter 'T'. |
| |
| Tried to infer 'num' for 'T' which doesn't work: |
| Parameter 'a' declared as 'T Function(T)' |
| but argument is 'double Function(int)'. |
| Parameter 'b' declared as 'T Function(T)' |
| but argument is 'int Function(double)'. |
| |
| 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. |
| var code = r''' |
| T max<T extends num>(T x, T y) => x; |
| |
| test() { |
| String hello = max(1, 2); |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 56, 5), |
| error(CompileTimeErrorCode.INVALID_CAST_LITERAL, 68, 1), |
| error(CompileTimeErrorCode.INVALID_CAST_LITERAL, 71, 1), |
| ]); |
| |
| var h = (AstFinder.getStatementsInTopLevelFunction(unit, "test")[0] |
| as VariableDeclarationStatement) |
| .variables |
| .variables[0]; |
| var call = h.initializer as MethodInvocation; |
| assertInvokeType(call, 'Null Function(Null, Null)'); |
| } |
| |
| test_inference_error_extendsFromReturn2() async { |
| var code = r''' |
| typedef R F<T, R>(T t); |
| F<T, T> g<T extends num>() => (y) => y; |
| |
| test() { |
| F<String, String> hello = g(); |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 94, 5), |
| error(CompileTimeErrorCode.COULD_NOT_INFER, 102, 1), |
| ]); |
| |
| _expectInferenceError(r''' |
| Couldn't infer type parameter 'T'. |
| |
| Tried to infer 'String' for 'T' which doesn't work: |
| Type parameter 'T' is declared to extend 'num' producing 'num'. |
| The type 'String' was inferred from: |
| Return type declared as 'T Function(T)' |
| used where 'String Function(String)' is required. |
| |
| Consider passing explicit type argument(s) to the generic. |
| |
| '''); |
| } |
| |
| test_inference_error_genericFunction() async { |
| var code = 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 assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 158, 1), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 195, 3), |
| error(CompileTimeErrorCode.COULD_NOT_INFER, 195, 3), |
| ]); |
| _expectInferenceError(r''' |
| Couldn't infer type parameter 'T'. |
| |
| Tried to infer 'dynamic' for 'T' which doesn't work: |
| Function type declared as 'T Function<T extends num>(T, T)' |
| used where 'num Function(num, dynamic)' is required. |
| |
| Consider passing explicit type argument(s) to the generic. |
| |
| '''); |
| } |
| |
| test_inference_error_returnContext() async { |
| var code = 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 assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 80, 1), |
| error(CompileTimeErrorCode.COULD_NOT_INFER, 84, 1), |
| ]); |
| _expectInferenceError(r''' |
| Couldn't infer type parameter 'T'. |
| |
| Tried to infer 'num' for 'T' which doesn't work: |
| Return type declared as 'T Function(T)' |
| used where 'int Function(num)' is required. |
| |
| Consider passing explicit type argument(s) to the generic. |
| |
| '''); |
| } |
| |
| test_inference_hints() async { |
| var code = r''' |
| void main () { |
| var x = 3; |
| List<int> l0 = []; |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 33, 1), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 58, 2), |
| ]); |
| } |
| |
| 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 code = 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); |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_ELEMENT, 5, 10), |
| ]); |
| |
| var body = AstFinder.getTopLevelFunction(unit, '_mergeSort') |
| .functionExpression |
| .body as BlockFunctionBody; |
| var stmts = body.block.statements.cast<ExpressionStatement>(); |
| for (ExpressionStatement stmt in stmts) { |
| var invoke = stmt.expression as MethodInvocation; |
| assertInvokeType(invoke, |
| 'void Function(T Function(T), int Function(T, T), T Function(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 code = 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); |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_ELEMENT, 5, 10), |
| ]); |
| |
| var body = AstFinder.getTopLevelFunction(unit, '_mergeSort') |
| .functionExpression |
| .body as BlockFunctionBody; |
| var stmts = body.block.statements.cast<ExpressionStatement>(); |
| for (ExpressionStatement stmt in stmts) { |
| var invoke = stmt.expression as MethodInvocation; |
| assertInvokeType( |
| invoke, 'void Function(List<T>, int Function(T, T), List<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 code = 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); |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_ELEMENT, 5, 10), |
| ]); |
| |
| var body = AstFinder.getTopLevelFunction(unit, '_mergeSort') |
| .functionExpression |
| .body as BlockFunctionBody; |
| var stmts = body.block.statements.cast<ExpressionStatement>(); |
| for (ExpressionStatement stmt in stmts) { |
| var invoke = stmt.expression as MethodInvocation; |
| assertInvokeType(invoke, 'void Function(T, int Function(T, T), T)'); |
| } |
| } |
| |
| test_inferGenericInstantiation() async { |
| // Verify that we don't infer '?` when we instantiate a generic function. |
| var code = r''' |
| T f<T>(T x(T t)) => x(null); |
| S g<S>(S s) => s; |
| test() { |
| var h = f(g); |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 61, 1), |
| ]); |
| |
| var h = (AstFinder.getStatementsInTopLevelFunction(unit, "test")[0] |
| as VariableDeclarationStatement) |
| .variables |
| .variables[0]; |
| _isDynamic(h.declaredElement!.type); |
| var fCall = h.initializer as MethodInvocation; |
| assertInvokeType(fCall, 'dynamic Function(dynamic Function(dynamic))'); |
| var g = fCall.argumentList.arguments[0]; |
| assertType(g.staticType, 'dynamic Function(dynamic)'); |
| } |
| |
| test_inferGenericInstantiation2() async { |
| // Verify the behavior when we cannot infer an instantiation due to invalid |
| // constraints from an outer generic method. |
| var code = 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); |
| '''; |
| await assertErrorsInCode(code, [ |
| error(CompileTimeErrorCode.COULD_NOT_INFER, 190, 3), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 190, 3), |
| ]); |
| |
| var fold = (AstFinder.getTopLevelFunction(unit, 'test') |
| .functionExpression |
| .body as ExpressionFunctionBody) |
| .expression as MethodInvocation; |
| assertInvokeType(fold, 'num Function(num, num Function(num, dynamic))'); |
| var max = fold.argumentList.arguments[1]; |
| // TODO(jmesserly): arguably num Function(num, num) is better here. |
| assertType(max.staticType, 'dynamic Function(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: [] }; |
| } |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| Asserter<InterfaceType> assertListOfInt = _isListOf(_isInt); |
| Asserter<InterfaceType> assertMapOfIntToListOfInt = _isMapOf( |
| _isInt, (DartType type) => assertListOfInt(type as InterfaceType)); |
| |
| VariableDeclaration mapB = AstFinder.getFieldInClass(unit, "B", "map"); |
| MethodDeclaration mapC = AstFinder.getMethodInClass(unit, "C", "map"); |
| assertMapOfIntToListOfInt(mapB.declaredElement!.type as InterfaceType); |
| assertMapOfIntToListOfInt( |
| mapC.declaredElement!.returnType as InterfaceType); |
| |
| var mapLiteralB = mapB.initializer as SetOrMapLiteral; |
| var mapLiteralC = |
| (mapC.body as ExpressionFunctionBody).expression as SetOrMapLiteral; |
| assertMapOfIntToListOfInt(mapLiteralB.staticType as InterfaceType); |
| assertMapOfIntToListOfInt(mapLiteralC.staticType as InterfaceType); |
| |
| var listLiteralB = |
| (mapLiteralB.elements[0] as MapLiteralEntry).value as ListLiteral; |
| var listLiteralC = |
| (mapLiteralC.elements[0] as MapLiteralEntry).value as ListLiteral; |
| assertListOfInt(listLiteralB.staticType as InterfaceType); |
| assertListOfInt(listLiteralC.staticType as InterfaceType); |
| } |
| |
| 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"); |
| }'''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 769, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 816, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 869, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 929, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 995, 2), |
| error(CompileTimeErrorCode.INVALID_CAST_NEW_EXPR, 1000, 31), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 1056, 2), |
| error(CompileTimeErrorCode.INVALID_CAST_NEW_EXPR, 1061, 41), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 1157, 2), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 1168, 7), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 1177, 1), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 1204, 2), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 1221, 7), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 1230, 1), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 1286, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 1333, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 1386, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 1446, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 1512, 2), |
| error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 1517, 34), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 1576, 2), |
| error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 1581, 41), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 1676, 2), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 1687, 1), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 1690, 7), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 1723, 2), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 1740, 1), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 1743, 7), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 1802, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 1837, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 1878, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 1918, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 1964, 2), |
| error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 1969, 17), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 2008, 2), |
| error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 2013, 23), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 2087, 2), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 2098, 7), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 2128, 2), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 2145, 7), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 2208, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 2252, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 2302, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 2359, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 2425, 2), |
| error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 2430, 28), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 2483, 2), |
| error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 2488, 38), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 2580, 2), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 2591, 1), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 2618, 2), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 2635, 1), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 2694, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 2805, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 2874, 2), |
| error(CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE, 2901, 7), |
| error(CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE, 2914, 1), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 2942, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 3007, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 3060, 2), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 3089, 7), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 3098, 1), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 3125, 2), |
| error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 3154, 7), |
| ]); |
| |
| Expression rhs(AstNode stmt) { |
| stmt as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| Expression exp = decl.initializer!; |
| return exp; |
| } |
| |
| void hasType(Asserter<DartType> assertion, Expression exp) => |
| assertion(exp.typeOrThrow); |
| |
| Element elementA = AstFinder.getClass(unit, "A").declaredElement!; |
| Element elementB = AstFinder.getClass(unit, "B").declaredElement!; |
| Element elementC = AstFinder.getClass(unit, "C").declaredElement!; |
| Element elementD = AstFinder.getClass(unit, "D").declaredElement!; |
| Element elementE = AstFinder.getClass(unit, "E").declaredElement!; |
| Element elementF = AstFinder.getClass(unit, "F").declaredElement!; |
| |
| 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") |
| .cast<VariableDeclarationStatement>(); |
| |
| 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") |
| .cast<VariableDeclarationStatement>(); |
| hasType(assertAOf([_isInt, _isString]), rhs(statements[0])); |
| hasType(assertAOf([_isInt, _isString]), rhs(statements[1])); |
| } |
| |
| { |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "test2") |
| .cast<VariableDeclarationStatement>(); |
| 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") |
| .cast<VariableDeclarationStatement>(); |
| hasType(assertBOf([_isString, _isInt]), rhs(statements[0])); |
| hasType(assertBOf([_isString, _isInt]), rhs(statements[1])); |
| } |
| |
| { |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "test4") |
| .cast<VariableDeclarationStatement>(); |
| 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") |
| .cast<VariableDeclarationStatement>(); |
| 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") |
| .cast<VariableDeclarationStatement>(); |
| 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") |
| .cast<VariableDeclarationStatement>(); |
| hasType(assertDOf([_isDynamic, _isString]), rhs(statements[0])); |
| hasType(assertDOf([_isDynamic, _isString]), rhs(statements[1])); |
| } |
| |
| { |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "test8") |
| .cast<VariableDeclarationStatement>(); |
| hasType(assertEOf([_isInt, _isString]), rhs(statements[0])); |
| } |
| |
| { |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "test9") |
| .cast<VariableDeclarationStatement>(); |
| 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], []]; |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 45, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 84, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 124, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 165, 2), |
| error(CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE, 172, 7), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| ListLiteral literal(int i) { |
| var stmt = statements[i] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| var exp = decl.initializer as ListLiteral; |
| return exp; |
| } |
| |
| Asserter<InterfaceType> assertListOfInt = _isListOf(_isInt); |
| Asserter<InterfaceType> assertListOfListOfInt = |
| _isListOf((DartType type) => assertListOfInt(type as InterfaceType)); |
| |
| assertListOfListOfInt(literal(0).staticType as InterfaceType); |
| assertListOfListOfInt(literal(1).staticType as InterfaceType); |
| assertListOfListOfInt(literal(2).staticType as InterfaceType); |
| assertListOfListOfInt(literal(3).staticType as InterfaceType); |
| |
| assertListOfInt( |
| (literal(1).elements[0] as Expression).staticType as InterfaceType); |
| assertListOfInt( |
| (literal(2).elements[0] as Expression).staticType as InterfaceType); |
| assertListOfInt( |
| (literal(3).elements[0] as Expression).staticType as InterfaceType); |
| } |
| |
| 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]; |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 39, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 66, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 94, 2), |
| error(CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE, 100, 7), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 128, 2), |
| error(CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE, 134, 7), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| DartType literal(int i) { |
| var stmt = statements[i] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| var exp = decl.initializer as ListLiteral; |
| return exp.typeOrThrow; |
| } |
| |
| Asserter<InterfaceType> assertListOfInt = _isListOf(_isInt); |
| |
| assertListOfInt(literal(0) as InterfaceType); |
| assertListOfInt(literal(1) as InterfaceType); |
| assertListOfInt(literal(2) as InterfaceType); |
| assertListOfInt(literal(3) as InterfaceType); |
| } |
| |
| 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]; |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 45, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 84, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 124, 2), |
| error(CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE, 136, 7), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 170, 2), |
| error(CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE, 182, 7), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| DartType literal(int i) { |
| var stmt = statements[i] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| var exp = decl.initializer as ListLiteral; |
| return exp.typeOrThrow; |
| } |
| |
| Asserter<InterfaceType> assertListOfInt = _isListOf(_isInt); |
| |
| assertListOfInt(literal(0) as InterfaceType); |
| assertListOfInt(literal(1) as InterfaceType); |
| assertListOfInt(literal(2) as InterfaceType); |
| assertListOfInt(literal(3) as InterfaceType); |
| } |
| |
| 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]; |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 39, 2), |
| error(CompileTimeErrorCode.INVALID_CAST_LITERAL_LIST, 44, 7), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 71, 2), |
| error(CompileTimeErrorCode.INVALID_CAST_LITERAL_LIST, 76, 8), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 104, 2), |
| error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 109, 17), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 146, 2), |
| error(CompileTimeErrorCode.INVALID_CAST_LITERAL_LIST, 151, 21), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| DartType literal(int i) { |
| var stmt = statements[i] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| var exp = decl.initializer as ListLiteral; |
| return exp.typeOrThrow; |
| } |
| |
| _isListOf(_isNum)(literal(0) as InterfaceType); |
| _isListOf(_isNum)(literal(1) as InterfaceType); |
| _isListOf(_isString)(literal(2) as InterfaceType); |
| _isListOf(_isDynamic)(literal(3) as InterfaceType); |
| } |
| |
| 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]; |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 43, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 74, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 106, 2), |
| error(CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE, 112, 7), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 144, 2), |
| error(CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE, 150, 7), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| InterfaceType literal(int i) { |
| var stmt = statements[i] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| var exp = decl.initializer as ListLiteral; |
| return exp.staticType as InterfaceType; |
| } |
| |
| 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]}; |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 52, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 92, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 144, 2), |
| error(CompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE, 150, 7), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 202, 2), |
| error(CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE, 212, 1), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 248, 2), |
| error(CompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE, 267, 7), |
| error(CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE, 277, 1), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| SetOrMapLiteral literal(int i) { |
| var stmt = statements[i] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| var exp = decl.initializer as SetOrMapLiteral; |
| return exp; |
| } |
| |
| Asserter<InterfaceType> assertListOfString = _isListOf(_isString); |
| Asserter<InterfaceType> assertMapOfIntToListOfString = _isMapOf( |
| _isInt, (DartType type) => assertListOfString(type as InterfaceType)); |
| |
| assertMapOfIntToListOfString(literal(0).staticType as InterfaceType); |
| assertMapOfIntToListOfString(literal(1).staticType as InterfaceType); |
| assertMapOfIntToListOfString(literal(2).staticType as InterfaceType); |
| assertMapOfIntToListOfString(literal(3).staticType as InterfaceType); |
| assertMapOfIntToListOfString(literal(4).staticType as InterfaceType); |
| |
| assertListOfString((literal(1).elements[0] as MapLiteralEntry) |
| .value |
| .staticType as InterfaceType); |
| assertListOfString((literal(2).elements[0] as MapLiteralEntry) |
| .value |
| .staticType as InterfaceType); |
| assertListOfString((literal(3).elements[0] as MapLiteralEntry) |
| .value |
| .staticType as InterfaceType); |
| assertListOfString((literal(4).elements[0] as MapLiteralEntry) |
| .value |
| .staticType as InterfaceType); |
| } |
| |
| 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}; |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 46, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 80, 2), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 124, 2), |
| error(CompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE, 130, 7), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 174, 2), |
| error(CompileTimeErrorCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE, 183, 1), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 212, 2), |
| error(CompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE, 229, 7), |
| error(CompileTimeErrorCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE, 238, 1), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| InterfaceType literal(int i) { |
| var stmt = statements[i] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| var exp = decl.initializer as SetOrMapLiteral; |
| return exp.staticType as InterfaceType; |
| } |
| |
| 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}; |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 46, 2), |
| error(CompileTimeErrorCode.INVALID_CAST_LITERAL_MAP, 51, 16), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 94, 2), |
| error(CompileTimeErrorCode.INVALID_CAST_LITERAL_MAP, 99, 26), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 152, 2), |
| error(CompileTimeErrorCode.INVALID_CAST_LITERAL_MAP, 157, 32), |
| error(CompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE, 172, 7), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 216, 2), |
| error(CompileTimeErrorCode.INVALID_CAST_LITERAL_MAP, 221, 20), |
| ]); |
| |
| List<Statement> statements = |
| AstFinder.getStatementsInTopLevelFunction(unit, "main"); |
| InterfaceType literal(int i) { |
| var stmt = statements[i] as VariableDeclarationStatement; |
| VariableDeclaration decl = stmt.variables.variables[0]; |
| var exp = decl.initializer as SetOrMapLiteral; |
| return exp.staticType as InterfaceType; |
| } |
| |
| 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];} |
| } |
| '''; |
| await assertErrorsInCode(code, [ |
| error(CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE, 101, 1), |
| ]); |
| |
| 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 as InterfaceType); |
| assertListOfString(methodReturnValue("m1").staticType as InterfaceType); |
| } |
| |
| 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); |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
| var body = test.functionExpression.body as ExpressionFunctionBody; |
| _isString(body.expression.typeOrThrow); |
| var invoke = body.expression as MethodInvocation; |
| var function = invoke.argumentList.arguments[0] as FunctionExpression; |
| ExecutableElement f0 = function.declaredElement!; |
| 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); |
| '''; |
| await assertErrorsInCode(code, [ |
| error(CompileTimeErrorCode.INVALID_CAST_LITERAL, 126, 1), |
| ]); |
| |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
| var body = test.functionExpression.body as ExpressionFunctionBody; |
| DartType type = body.expression.typeOrThrow; |
| |
| Element elementB = AstFinder.getClass(unit, "B").declaredElement!; |
| |
| _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); |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
| var body = test.functionExpression.body as ExpressionFunctionBody; |
| DartType type = body.expression.typeOrThrow; |
| |
| Element elementB = AstFinder.getClass(unit, "B").declaredElement!; |
| |
| _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); |
| '''; |
| await assertErrorsInCode(code, [ |
| error(CompileTimeErrorCode.INVALID_CAST_LITERAL, 126, 1), |
| ]); |
| |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
| var body = test.functionExpression.body as ExpressionFunctionBody; |
| DartType type = body.expression.typeOrThrow; |
| |
| Element elementB = AstFinder.getClass(unit, "B").declaredElement!; |
| |
| _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(); |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
| var body = test.functionExpression.body as ExpressionFunctionBody; |
| DartType type = body.expression.typeOrThrow; |
| |
| Element elementB = AstFinder.getClass(unit, "B").declaredElement!; |
| |
| _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(); |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
| var body = test.functionExpression.body as ExpressionFunctionBody; |
| var functionType = body.expression.staticType as FunctionType; |
| DartType type = functionType.normalParameterTypes[0]; |
| |
| Element elementA = AstFinder.getClass(unit, "A").declaredElement!; |
| |
| _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(); |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
| var body = test.functionExpression.body as ExpressionFunctionBody; |
| var functionType = body.expression.staticType as FunctionType; |
| DartType type = functionType.normalParameterTypes[0]; |
| |
| Element elementA = AstFinder.getClass(unit, "A").declaredElement!; |
| |
| _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(); |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
| var body = test.functionExpression.body as ExpressionFunctionBody; |
| var functionType = body.expression.staticType as FunctionType; |
| DartType type = functionType.normalParameterTypes[0]; |
| |
| Element elementA = AstFinder.getClass(unit, "A").declaredElement!; |
| |
| _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(); |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
| var body = test.functionExpression.body as ExpressionFunctionBody; |
| var functionType = body.expression.staticType as FunctionType; |
| DartType type = functionType.normalParameterTypes[0]; |
| |
| Element elementA = AstFinder.getClass(unit, "A").declaredElement!; |
| |
| _isInstantiationOf(_hasElement(elementA))([_isNum, _isNum])(type); |
| } |
| |
| test_redirectedConstructor_named() async { |
| var code = r''' |
| class A<T, U> implements B<T, U> { |
| A.named(); |
| } |
| |
| class B<T2, U2> { |
| factory B() = A.named; |
| } |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| var b = unit.declarations[1] as ClassDeclaration; |
| var bConstructor = b.members[0] as ConstructorDeclaration; |
| var redirected = bConstructor.redirectedConstructor as ConstructorName; |
| |
| var typeName = redirected.type; |
| assertType(typeName.type, 'A<T2, U2>'); |
| assertType(typeName.type, 'A<T2, U2>'); |
| |
| var constructorMember = redirected.staticElement!; |
| expect( |
| constructorMember.getDisplayString(withNullability: false), |
| 'A<T2, U2> A.named()', |
| ); |
| expect(redirected.name!.staticElement, constructorMember); |
| } |
| |
| test_redirectedConstructor_self() async { |
| await assertNoErrorsInCode(r''' |
| class A<T> { |
| A(); |
| factory A.redirected() = A; |
| } |
| '''); |
| } |
| |
| test_redirectedConstructor_unnamed() async { |
| await assertNoErrorsInCode(r''' |
| class A<T, U> implements B<T, U> { |
| A(); |
| } |
| |
| class B<T2, U2> { |
| factory B() = A; |
| } |
| '''); |
| |
| var b = result.unit.declarations[1] as ClassDeclaration; |
| var bConstructor = b.members[0] as ConstructorDeclaration; |
| var redirected = bConstructor.redirectedConstructor as ConstructorName; |
| |
| var typeName = redirected.type; |
| assertType(typeName.type, 'A<T2, U2>'); |
| assertType(typeName.type, 'A<T2, U2>'); |
| |
| expect(redirected.name, isNull); |
| expect( |
| redirected.staticElement!.getDisplayString(withNullability: false), |
| 'A<T2, U2> A()', |
| ); |
| } |
| |
| test_redirectingConstructor_propagation() async { |
| String code = r''' |
| class A { |
| A() : this.named([]); |
| A.named(List<String> x); |
| } |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| ConstructorDeclaration constructor = |
| AstFinder.getConstructorInClass(unit, "A", null); |
| var invocation = |
| constructor.initializers[0] as RedirectingConstructorInvocation; |
| Expression exp = invocation.argumentList.arguments[0]; |
| _isListOf(_isString)(exp.staticType as InterfaceType); |
| } |
| |
| 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); |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
| var body = test.functionExpression.body as ExpressionFunctionBody; |
| var invoke = body.expression as MethodInvocation; |
| _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); |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
| var body = test.functionExpression.body as ExpressionFunctionBody; |
| var invoke = body.expression as MethodInvocation; |
| _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); |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
| var body = test.functionExpression.body as ExpressionFunctionBody; |
| var functionType = body.expression.staticType as FunctionType; |
| 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); |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
| var body = test.functionExpression.body as ExpressionFunctionBody; |
| var functionType = body.expression.staticType as FunctionType; |
| 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)); |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
| var body = test.functionExpression.body as ExpressionFunctionBody; |
| var call = body.expression as MethodInvocation; |
| _isNum(call.typeOrThrow); |
| _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)); |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
| var body = test.functionExpression.body as ExpressionFunctionBody; |
| var call = body.expression as MethodInvocation; |
| _isNum(call.typeOrThrow); |
| _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([]); |
| } |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| ConstructorDeclaration constructor = |
| AstFinder.getConstructorInClass(unit, "A", null); |
| var invocation = constructor.initializers[0] as SuperConstructorInvocation; |
| Expression exp = invocation.argumentList.arguments[0]; |
| _isListOf(_isString)(exp.staticType as InterfaceType); |
| } |
| |
| /// Verifies the result has [CompileTimeErrorCode.COULD_NOT_INFER] with |
| /// the expected [errorMessage]. |
| void _expectInferenceError(String errorMessage) { |
| var errors = result.errors |
| .where((e) => e.errorCode == CompileTimeErrorCode.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<ExpectedError> expectedErrors = const []}) async { |
| var fullCode = """ |
| import "dart:async"; |
| |
| $code |
| """; |
| await assertErrorsInCode(fullCode, expectedErrors); |
| |
| FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
| var body = test.functionExpression.body as ExpressionFunctionBody; |
| return body.expression as MethodInvocation; |
| } |
| } |
| |
| @reflectiveTest |
| class StrongModeStaticTypeAnalyzer2Test extends StaticTypeAnalyzer2TestShared { |
| void expectStaticInvokeType(String search, String expected) { |
| var invocation = findNode.simple(search).parent as MethodInvocation; |
| assertInvokeType(invocation, expected); |
| } |
| |
| test_dynamicObjectGetter_hashCode() async { |
| await assertErrorsInCode(r''' |
| main() { |
| dynamic a = null; |
| var foo = a.hashCode; |
| } |
| ''', [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 35, 3), |
| ]); |
| expectInitializerType('foo', 'int'); |
| } |
| |
| 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 assertNoErrorsInCode(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 assertNoErrorsInCode(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 assertNoErrorsInCode(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 assertNoErrorsInCode(code); |
| } |
| |
| test_generalizedVoid_assignToVoidOk() async { |
| await assertErrorsInCode(r''' |
| void main() { |
| void x; |
| x = 42; |
| } |
| ''', [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 21, 1), |
| ]); |
| } |
| |
| test_genericFunction() async { |
| await assertNoErrorsInCode(r'T f<T>(T x) => null;'); |
| expectFunctionType('f', 'T Function<T>(T)', typeFormals: '[T]'); |
| SimpleIdentifier f = findNode.simple('f'); |
| var e = f.staticElement as FunctionElementImpl; |
| FunctionType ft = e.type.instantiate([typeProvider.stringType]); |
| assertType(ft, 'String Function(String)'); |
| } |
| |
| test_genericFunction_bounds() async { |
| await assertNoErrorsInCode(r'T f<T extends num>(T x) => null;'); |
| expectFunctionType('f', 'T Function<T extends num>(T)', |
| typeFormals: '[T extends num]'); |
| } |
| |
| test_genericFunction_parameter() async { |
| await assertNoErrorsInCode(r''' |
| void g(T f<T>(T x)) {} |
| '''); |
| var type = expectFunctionType2('f', 'T Function<T>(T)'); |
| FunctionType ft = type.instantiate([typeProvider.stringType]); |
| assertType(ft, 'String Function(String)'); |
| } |
| |
| test_genericFunction_static() async { |
| await assertNoErrorsInCode(r''' |
| class C<E> { |
| static T f<T>(T x) => null; |
| } |
| '''); |
| expectFunctionType('f', 'T Function<T>(T)', typeFormals: '[T]'); |
| SimpleIdentifier f = findNode.simple('f'); |
| var e = f.staticElement as MethodElementImpl; |
| FunctionType ft = e.type.instantiate([typeProvider.stringType]); |
| assertType(ft, 'String Function(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) { // C |
| F f4; |
| f0(3); |
| f1(3); |
| f2(3); |
| f3(3); |
| f4(3); |
| } |
| } |
| |
| class D<S> { |
| static F f1; |
| F f2; |
| void g(F f3) { // D |
| F f4; |
| f0(3); |
| f1(3); |
| f2(3); |
| f3(3); |
| f4(3); |
| } |
| } |
| '''; |
| await assertNoErrorsInCode(code); |
| |
| checkBody(String className) { |
| var statements = findNode.block('{ // $className').statements; |
| |
| 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 assertNoErrorsInCode(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 assertNoErrorsInCode(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 assertErrorsInCode(r''' |
| class C<E> { |
| List<T> f<T>(E e) => null; |
| } |
| main() { |
| C<String> cOfString; |
| } |
| ''', [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 65, 9), |
| ]); |
| assertType(findElement.method('f').type, 'List<T> Function<T>(E)'); |
| |
| var cOfString = findElement.localVar('cOfString'); |
| var ft = (cOfString.type as InterfaceType).getMethod('f')!.type; |
| assertType(ft, 'List<T> Function<T>(String)'); |
| assertType( |
| ft.instantiate([typeProvider.intType]), 'List<int> Function(String)'); |
| } |
| |
| test_genericMethod_explicitTypeParams() async { |
| await assertErrorsInCode(r''' |
| class C<E> { |
| List<T> f<T>(E e) => null; |
| } |
| main() { |
| C<String> cOfString; |
| var x = cOfString.f<int>('hi'); |
| } |
| ''', [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 82, 1), |
| ]); |
| var f = findNode.simple('f<int>').parent as MethodInvocation; |
| var ft = f.staticInvokeType as FunctionType; |
| assertType(ft, 'List<int> Function(String)'); |
| |
| var x = findElement.localVar('x'); |
| expect(x.type, typeProvider.listType(typeProvider.intType)); |
| } |
| |
| test_genericMethod_functionExpressionInvocation_explicit() async { |
| await assertErrorsInCode(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); |
| } |
| ''', [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 237, 10), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 281, 10), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 315, 10), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 349, 15), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 388, 10), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 423, 12), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 460, 9), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 492, 9), |
| ]); |
| _assertLocalVarType('lambdaCall', "int"); |
| _assertLocalVarType('methodCall', "int"); |
| _assertLocalVarType('staticCall', "int"); |
| _assertLocalVarType('staticFieldCall', "int"); |
| _assertLocalVarType('topFunCall', "int"); |
| _assertLocalVarType('topFieldCall', "int"); |
| _assertLocalVarType('localCall', "int"); |
| _assertLocalVarType('paramCall', "int"); |
| } |
| |
| test_genericMethod_functionExpressionInvocation_functionTypedParameter_explicit() async { |
| await assertErrorsInCode(r''' |
| void test<S>(T pf<T>(T e)) { |
| var paramCall = (pf)<int>(3); |
| } |
| ''', [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 35, 9), |
| ]); |
| _assertLocalVarType('paramCall', "int"); |
| } |
| |
| test_genericMethod_functionExpressionInvocation_functionTypedParameter_inferred() async { |
| await assertErrorsInCode(r''' |
| void test<S>(T pf<T>(T e)) { |
| var paramCall = (pf)(3); |
| } |
| ''', [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 35, 9), |
| ]); |
| _assertLocalVarType('paramCall', "int"); |
| } |
| |
| test_genericMethod_functionExpressionInvocation_inferred() async { |
| await assertErrorsInCode(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); |
| } |
| ''', [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 237, 10), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 276, 10), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 305, 10), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 334, 15), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 368, 10), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 398, 12), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 430, 9), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 457, 9), |
| ]); |
| _assertLocalVarType('lambdaCall', "int"); |
| _assertLocalVarType('methodCall', "int"); |
| _assertLocalVarType('staticCall', "int"); |
| _assertLocalVarType('staticFieldCall', "int"); |
| _assertLocalVarType('topFunCall', "int"); |
| _assertLocalVarType('topFieldCall', "int"); |
| _assertLocalVarType('localCall', "int"); |
| _assertLocalVarType('paramCall', "int"); |
| } |
| |
| test_genericMethod_functionInvocation_explicit() async { |
| await assertErrorsInCode(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); |
| } |
| ''', [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 236, 10), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 268, 10), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 300, 15), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 337, 10), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 370, 12), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 405, 9), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 435, 9), |
| ]); |
| _assertLocalVarType('methodCall', "int"); |
| _assertLocalVarType('staticCall', "int"); |
| _assertLocalVarType('staticFieldCall', "int"); |
| _assertLocalVarType('topFunCall', "int"); |
| _assertLocalVarType('topFieldCall', "int"); |
| _assertLocalVarType('localCall', "int"); |
| _assertLocalVarType('paramCall', "int"); |
| } |
| |
| test_genericMethod_functionInvocation_functionTypedParameter_explicit() async { |
| await assertErrorsInCode(r''' |
| void test<S>(T pf<T>(T e)) { |
| var paramCall = pf<int>(3); |
| } |
| ''', [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 35, 9), |
| ]); |
| _assertLocalVarType('paramCall', "int"); |
| } |
| |
| test_genericMethod_functionInvocation_functionTypedParameter_inferred() async { |
| await assertErrorsInCode(r''' |
| void test<S>(T pf<T>(T e)) { |
| var paramCall = pf(3); |
| } |
| ''', [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 35, 9), |
| ]); |
| _assertLocalVarType('paramCall', "int"); |
| } |
| |
| test_genericMethod_functionInvocation_inferred() async { |
| await assertErrorsInCode(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); |
| } |
| ''', [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 236, 10), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 263, 10), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 290, 15), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 322, 10), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 350, 12), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 380, 9), |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 405, 9), |
| ]); |
| _assertLocalVarType('methodCall', "int"); |
| _assertLocalVarType('staticCall', "int"); |
| _assertLocalVarType('staticFieldCall', "int"); |
| _assertLocalVarType('topFunCall', "int"); |
| _assertLocalVarType('topFieldCall', "int"); |
| _assertLocalVarType('localCall', "int"); |
| _assertLocalVarType('paramCall', "int"); |
| } |
| |
| test_genericMethod_functionTypedParameter() async { |
| await assertErrorsInCode(r''' |
| class C<E> { |
| List<T> f<T>(T f(E e)) => null; |
| } |
| main() { |
| C<String> cOfString; |
| } |
| ''', [ |
| error(HintCode.UNUSED_LOCAL_VARIABLE, 70, 9), |
| ]); |
| assertType( |
| findElement.method('f').type, 'List<T> Function<T>(T Function(E))'); |
| |
| var cOfString = findElement.localVar('cOfString'); |
| var ft = (cOfString.type as InterfaceType).getMethod('f')!.type; |
| assertType(ft, 'List<T> Function<T>(T Function(String))'); |
| assertType(ft.instantiate([typeProvider.int
|