Index: pkg/analyzer/test/generated/strong_mode_test.dart |
diff --git a/pkg/analyzer/test/generated/strong_mode_test.dart b/pkg/analyzer/test/generated/strong_mode_test.dart |
index 81f02697c62e11cc1be282377edf02fe7427461a..2dd77c61888126eb6ecfe69342e4994a9ec03e2b 100644 |
--- a/pkg/analyzer/test/generated/strong_mode_test.dart |
+++ b/pkg/analyzer/test/generated/strong_mode_test.dart |
@@ -22,17 +22,17 @@ import 'resolver_test_case.dart'; |
main() { |
defineReflectiveSuite(() { |
- defineReflectiveTests(StrongModeDownwardsInferenceTest); |
+ defineReflectiveTests(StrongModeLocalInferenceTest); |
defineReflectiveTests(StrongModeStaticTypeAnalyzer2Test); |
defineReflectiveTests(StrongModeTypePropagationTest); |
}); |
} |
/** |
- * Strong mode static analyzer downwards inference tests |
+ * Strong mode static analyzer local type inference tests |
*/ |
@reflectiveTest |
-class StrongModeDownwardsInferenceTest extends ResolverTestCase { |
+class StrongModeLocalInferenceTest extends ResolverTestCase { |
TypeAssertions _assertions; |
Asserter<DartType> _isDynamic; |
@@ -40,6 +40,7 @@ class StrongModeDownwardsInferenceTest extends ResolverTestCase { |
Asserter<InterfaceType> _isFutureOfInt; |
Asserter<DartType> _isInt; |
Asserter<DartType> _isNum; |
+ Asserter<DartType> _isObject; |
Asserter<DartType> _isString; |
AsserterBuilder2<Asserter<DartType>, Asserter<DartType>, DartType> |
@@ -66,6 +67,7 @@ class StrongModeDownwardsInferenceTest extends ResolverTestCase { |
_isInstantiationOf = _assertions.isInstantiationOf; |
_isInt = _assertions.isInt; |
_isNum = _assertions.isNum; |
+ _isObject = _assertions.isObject; |
_isString = _assertions.isString; |
_isDynamic = _assertions.isDynamic; |
_isListOf = _assertions.isListOf; |
@@ -80,6 +82,176 @@ class StrongModeDownwardsInferenceTest extends ResolverTestCase { |
return result; |
} |
+ fail_constrainedByBounds3() async { |
+ // Test that upwards inference with two type variables does |
+ // not propogate from the constrained variable to the unconstrained |
+ // variable if they are ordered right to left, and that if the result |
+ // is not a valid instantiation an error is issued |
+ String code = r''' |
+ T f<T extends S, S extends int>(S x) => null; |
+ void test() { var x = f(3); } |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertErrors( |
+ source, [StaticTypeWarningCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS]); |
Jennifer Messerly
2017/01/24 19:03:35
might be worth a comment that the error is what we
Leaf
2017/01/24 19:23:30
I think that's already in the comment, and in the
|
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ List<Statement> statements = |
+ AstFinder.getStatementsInTopLevelFunction(unit, "test"); |
+ VariableDeclarationStatement stmt = statements[0]; |
+ VariableDeclaration decl = stmt.variables.variables[0]; |
+ Expression call = decl.initializer; |
+ _isDynamic(call.staticType); |
+ } |
+ |
+ fail_constrainedByBounds5() async { |
+ // Test that upwards inference with two type variables does not |
+ // propogate from the constrained variable to the unconstrained |
+ // variable if they are ordered right to left, when the variable |
+ // appears co and contra variantly, and that an error is issued |
+ // for the non-matching bound. |
+ String code = r''' |
+ typedef To Func1<From, To>(From x); |
+ T f<T extends Func1<S, S>, S>(S x) => null; |
+ void test() { var x = f(3)(4); } |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertErrors( |
+ source, [StaticTypeWarningCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS]); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ List<Statement> statements = |
+ AstFinder.getStatementsInTopLevelFunction(unit, "test"); |
+ VariableDeclarationStatement stmt = statements[0]; |
+ VariableDeclaration decl = stmt.variables.variables[0]; |
+ Expression call = decl.initializer; |
+ _isInt(call.staticType); |
+ } |
+ |
+ fail_pinning_multipleConstraints1() async { |
+ // Test that downwards inference with two different downwards covariant |
+ // constraints on the same parameter correctly fails to infer when |
+ // the types do not share a common subtype |
+ String code = r''' |
+ class A<S, T> { |
+ S s; |
+ T t; |
+ } |
+ class B<S> extends A<S, S> { B(S s); } |
+ A<int, String> test() => new B(3); |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertErrors(source, [StrongModeCode.COULD_NOT_INFER]); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
+ ExpressionFunctionBody body = test.functionExpression.body; |
+ DartType type = body.expression.staticType; |
+ |
+ Element elementB = AstFinder.getClass(unit, "B").element; |
+ |
+ _isInstantiationOf(_hasElement(elementB))([_isDynamic])(type); |
Jennifer Messerly
2017/01/24 19:03:35
FYI: we don't pick "dynamic" in case of inference
Leaf
2017/01/24 19:23:30
Are you saying the expectation of B<dynamic> is wr
|
+ } |
+ |
+ fail_pinning_multipleConstraints2() async { |
+ // Test that downwards inference with two identical downwards covariant |
+ // constraints on the same parameter correctly infers and pins the type |
+ String code = r''' |
+ class A<S, T> { |
+ S s; |
+ T t; |
+ } |
+ class B<S> extends A<S, S> { B(S s); } |
+ A<num, num> test() => new B(3); |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertNoErrors(source); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
+ ExpressionFunctionBody body = test.functionExpression.body; |
+ DartType type = body.expression.staticType; |
+ |
+ Element elementB = AstFinder.getClass(unit, "B").element; |
+ |
+ _isInstantiationOf(_hasElement(elementB))([_isNum])(type); |
+ } |
+ |
+ fail_pinning_multipleConstraints3() async { |
+ // Test that downwards inference with two different downwards covariant |
+ // constraints on the same parameter correctly fails to infer when |
+ // the types do not share a common subtype, but do share a common supertype |
+ String code = r''' |
+ class A<S, T> { |
+ S s; |
+ T t; |
+ } |
+ class B<S> extends A<S, S> { B(S s); } |
+ A<int, double> test() => new B(3); |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertErrors(source, [ |
+ StrongModeCode.COULD_NOT_INFER, |
+ StaticTypeWarningCode.RETURN_OF_INVALID_TYPE |
+ ]); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
+ ExpressionFunctionBody body = test.functionExpression.body; |
+ DartType type = body.expression.staticType; |
+ |
+ Element elementB = AstFinder.getClass(unit, "B").element; |
+ |
+ _isInstantiationOf(_hasElement(elementB))([_isDynamic])(type); |
+ } |
+ |
+ fail_returnType_variance2() async { |
+ // Check that downwards inference correctly pins a type parameter |
+ // when the parameter is constrained in a covariant position |
+ String code = r''' |
+ typedef To Func1<From, To>(From x); |
+ Func1<String, T> f<T>(T x) => null; |
+ Func1<String, num> test() => f(42); |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertNoErrors(source); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
+ ExpressionFunctionBody body = test.functionExpression.body; |
+ MethodInvocation invoke = body.expression; |
+ _isFunction2Of(_isNum, _isFunction2Of(_isString, _isNum))( |
+ invoke.staticInvokeType); |
+ } |
+ |
+ fail_returnType_variance6() async { |
+ // Check that pinning works correctly with a partial type |
+ // when the return type uses the variable in a covariant position |
+ String code = r''' |
+ typedef To Func1<From, To>(From x); |
+ Func1<String, T> f<T>(T x) => null; |
+ T g<T, S>(Func1<S, T> f) => null; |
+ num test() => g(f(3)); |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertNoErrors(source); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
+ ExpressionFunctionBody body = test.functionExpression.body; |
+ MethodInvocation call = body.expression; |
+ _isNum(call.staticType); |
+ _isFunction2Of(_isFunction2Of(_isString, _isNum), _isNum)( |
+ call.staticInvokeType); |
+ } |
+ |
@override |
void setUp() { |
super.setUp(); |
@@ -287,6 +459,71 @@ class StrongModeDownwardsInferenceTest extends ResolverTestCase { |
expect(f0.type.normalParameterTypes[0], typeProvider.intType); |
} |
+ test_constrainedByBounds1() async { |
+ // Test that upwards inference with two type variables correctly |
+ // propogates from the constrained variable to the unconstrained |
+ // variable if they are ordered left to right. |
+ String code = r''' |
+ T f<S, T extends S>(S x) => null; |
+ void test() { var x = f(3); } |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertNoErrors(source); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ List<Statement> statements = |
+ AstFinder.getStatementsInTopLevelFunction(unit, "test"); |
+ VariableDeclarationStatement stmt = statements[0]; |
+ VariableDeclaration decl = stmt.variables.variables[0]; |
+ Expression call = decl.initializer; |
+ _isInt(call.staticType); |
+ } |
+ |
+ test_constrainedByBounds2() async { |
+ // Test that upwards inference with two type variables does |
+ // not propogate from the constrained variable to the unconstrained |
+ // variable if they are ordered right to left. |
+ String code = r''' |
+ T f<T extends S, S>(S x) => null; |
+ void test() { var x = f(3); } |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertNoErrors(source); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ List<Statement> statements = |
+ AstFinder.getStatementsInTopLevelFunction(unit, "test"); |
+ VariableDeclarationStatement stmt = statements[0]; |
+ VariableDeclaration decl = stmt.variables.variables[0]; |
+ Expression call = decl.initializer; |
+ _isDynamic(call.staticType); |
+ } |
+ |
+ test_constrainedByBounds4() async { |
+ // Test that upwards inference with two type variables correctly |
+ // propogates from the constrained variable to the unconstrained |
+ // variable if they are ordered left to right, when the variable |
+ // appears co and contra variantly |
+ String code = r''' |
+ typedef To Func1<From, To>(From x); |
+ T f<S, T extends Func1<S, S>>(S x) => null; |
+ void test() { var x = f(3)(4); } |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertNoErrors(source); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ List<Statement> statements = |
+ AstFinder.getStatementsInTopLevelFunction(unit, "test"); |
+ VariableDeclarationStatement stmt = statements[0]; |
+ VariableDeclaration decl = stmt.variables.variables[0]; |
+ Expression call = decl.initializer; |
+ _isInt(call.staticType); |
+ } |
+ |
test_constructorInitializer_propagation() async { |
String code = r''' |
class A { |
@@ -1212,6 +1449,171 @@ class StrongModeDownwardsInferenceTest extends ResolverTestCase { |
assertListOfString(methodReturnValue("m1").staticType); |
} |
+ test_partialTypes1() async { |
+ // Test that downwards inference with a partial type |
+ // correctly uses the partial information to fill in subterm |
+ // types |
+ String code = r''' |
+ typedef To Func1<From, To>(From x); |
+ S f<S, T>(Func1<S, T> g) => null; |
+ String test() => f((l) => l.length); |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertNoErrors(source); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
+ ExpressionFunctionBody body = test.functionExpression.body; |
+ _isString(body.expression.staticType); |
+ MethodInvocation invoke = body.expression; |
+ FunctionExpression function = invoke.argumentList.arguments[0]; |
+ ExecutableElement f0 = function.element; |
+ FunctionType type = f0.type; |
+ _isFunction2Of(_isString, _isInt)(type); |
+ } |
+ |
+ test_pinning_multipleConstraints4() async { |
+ // Test that downwards inference with two subtype related downwards |
+ // covariant constraints on the same parameter correctly infers and pins |
+ // the type |
+ String code = r''' |
+ class A<S, T> { |
+ S s; |
+ T t; |
+ } |
+ class B<S> extends A<S, S> {} |
+ A<int, num> test() => new B(); |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertNoErrors(source); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
+ ExpressionFunctionBody body = test.functionExpression.body; |
+ DartType type = body.expression.staticType; |
+ |
+ Element elementB = AstFinder.getClass(unit, "B").element; |
+ |
+ _isInstantiationOf(_hasElement(elementB))([_isInt])(type); |
+ } |
+ |
+ test_pinning_multipleConstraints_contravariant1() async { |
+ // Test that downwards inference with two different downwards contravariant |
+ // constraints on the same parameter chooses the upper bound |
+ // when the only supertype is Object |
+ String code = r''' |
+ class A<S, T> { |
+ S s; |
+ T t; |
+ } |
+ class B<S> extends A<S, S> {} |
+ typedef void Contra1<T>(T x); |
+ Contra1<A<S, S>> mkA<S>() => (A<S, S> x) {}; |
+ Contra1<A<int, String>> test() => mkA(); |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertNoErrors(source); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
+ ExpressionFunctionBody body = test.functionExpression.body; |
+ FunctionType functionType = body.expression.staticType; |
+ DartType type = functionType.normalParameterTypes[0]; |
+ |
+ Element elementA = AstFinder.getClass(unit, "A").element; |
+ |
+ _isInstantiationOf(_hasElement(elementA))([_isObject, _isObject])(type); |
+ } |
+ |
+ test_pinning_multipleConstraints_contravariant2() async { |
+ // Test that downwards inference with two identical downwards contravariant |
+ // constraints on the same parameter correctly pins the type |
+ String code = r''' |
+ class A<S, T> { |
+ S s; |
+ T t; |
+ } |
+ class B<S> extends A<S, S> {} |
+ typedef void Contra1<T>(T x); |
+ Contra1<A<S, S>> mkA<S>() => (A<S, S> x) {}; |
+ Contra1<A<num, num>> test() => mkA(); |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertNoErrors(source); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
+ ExpressionFunctionBody body = test.functionExpression.body; |
+ FunctionType functionType = body.expression.staticType; |
+ DartType type = functionType.normalParameterTypes[0]; |
+ |
+ Element elementA = AstFinder.getClass(unit, "A").element; |
+ |
+ _isInstantiationOf(_hasElement(elementA))([_isNum, _isNum])(type); |
+ } |
+ |
+ test_pinning_multipleConstraints_contravariant3() async { |
+ // Test that downwards inference with two different downwards contravariant |
+ // constraints on the same parameter correctly choose the least upper bound |
+ // when they share a common supertype |
+ String code = r''' |
+ class A<S, T> { |
+ S s; |
+ T t; |
+ } |
+ class B<S> extends A<S, S> {} |
+ typedef void Contra1<T>(T x); |
+ Contra1<A<S, S>> mkA<S>() => (A<S, S> x) {}; |
+ Contra1<A<int, double>> test() => mkA(); |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertNoErrors(source); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
+ ExpressionFunctionBody body = test.functionExpression.body; |
+ FunctionType functionType = body.expression.staticType; |
+ DartType type = functionType.normalParameterTypes[0]; |
+ |
+ Element elementA = AstFinder.getClass(unit, "A").element; |
+ |
+ _isInstantiationOf(_hasElement(elementA))([_isNum, _isNum])(type); |
+ } |
+ |
+ test_pinning_multipleConstraints_contravariant4() async { |
+ // Test that downwards inference with two different downwards contravariant |
+ // constraints on the same parameter correctly choose the least upper bound |
+ // when one is a subtype of the other |
+ String code = r''' |
+ class A<S, T> { |
+ S s; |
+ T t; |
+ } |
+ class B<S> extends A<S, S> {} |
+ typedef void Contra1<T>(T x); |
+ Contra1<A<S, S>> mkA<S>() => (A<S, S> x) {}; |
+ Contra1<A<int, num>> test() => mkA(); |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertNoErrors(source); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
+ ExpressionFunctionBody body = test.functionExpression.body; |
+ FunctionType functionType = body.expression.staticType; |
+ DartType type = functionType.normalParameterTypes[0]; |
+ |
+ Element elementA = AstFinder.getClass(unit, "A").element; |
+ |
+ _isInstantiationOf(_hasElement(elementA))([_isNum, _isNum])(type); |
+ } |
+ |
test_redirectingConstructor_propagation() async { |
String code = r''' |
class A { |
@@ -1228,6 +1630,90 @@ class StrongModeDownwardsInferenceTest extends ResolverTestCase { |
_isListOf(_isString)(exp.staticType); |
} |
+ test_returnType_variance1() async { |
+ // Check that downwards inference correctly pins a type parameter |
+ // when the parameter is constrained in a contravariant position |
+ String code = r''' |
+ typedef To Func1<From, To>(From x); |
+ Func1<T, String> f<T>(T x) => null; |
+ Func1<num, String> test() => f(42); |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertNoErrors(source); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
+ ExpressionFunctionBody body = test.functionExpression.body; |
+ MethodInvocation invoke = body.expression; |
+ _isFunction2Of(_isNum, _isFunction2Of(_isNum, _isString))( |
+ invoke.staticInvokeType); |
+ } |
+ |
+ test_returnType_variance3() async { |
+ // Check that the variance heuristic chooses the less precise type |
+ // when the return type uses the variable in a contravariant position |
+ // and there is no downwards constraint. |
+ String code = r''' |
+ typedef To Func1<From, To>(From x); |
+ Func1<T, String> f<T>(T x, g(T x)) => null; |
+ dynamic test() => f(42, (num x) => x); |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertNoErrors(source); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
+ ExpressionFunctionBody body = test.functionExpression.body; |
+ FunctionType functionType = body.expression.staticType; |
+ DartType type = functionType.normalParameterTypes[0]; |
+ _isNum(type); |
+ } |
+ |
+ test_returnType_variance4() async { |
+ // Check that the variance heuristic chooses the more precise type |
+ // when the return type uses the variable in a covariant position |
+ // and there is no downwards constraint |
+ String code = r''' |
+ typedef To Func1<From, To>(From x); |
+ Func1<String, T> f<T>(T x, g(T x)) => null; |
+ dynamic test() => f(42, (num x) => x); |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertNoErrors(source); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
+ ExpressionFunctionBody body = test.functionExpression.body; |
+ FunctionType functionType = body.expression.staticType; |
+ DartType type = functionType.returnType; |
+ _isInt(type); |
+ } |
+ |
+ test_returnType_variance5() async { |
+ // Check that pinning works correctly with a partial type |
+ // when the return type uses the variable in a contravariant position |
+ String code = r''' |
+ typedef To Func1<From, To>(From x); |
+ Func1<T, String> f<T>(T x) => null; |
+ T g<T, S>(Func1<T, S> f) => null; |
+ num test() => g(f(3)); |
+ '''; |
+ Source source = addSource(code); |
+ TestAnalysisResult analysisResult = await computeAnalysisResult(source); |
+ assertNoErrors(source); |
+ verify([source]); |
+ CompilationUnit unit = analysisResult.unit; |
+ FunctionDeclaration test = AstFinder.getTopLevelFunction(unit, "test"); |
+ ExpressionFunctionBody body = test.functionExpression.body; |
+ MethodInvocation call = body.expression; |
+ _isNum(call.staticType); |
+ _isFunction2Of(_isFunction2Of(_isNum, _isString), _isNum)( |
+ call.staticInvokeType); |
+ } |
+ |
test_superConstructorInvocation_propagation() async { |
String code = r''' |
class B { |