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 848668e6e6b7c0180def4e0ed0cca91f7ba4b03f..4bd8d01c7a0abcfe974e24a436cd77346072fbd6 100644 |
--- a/pkg/analyzer/test/generated/strong_mode_test.dart |
+++ b/pkg/analyzer/test/generated/strong_mode_test.dart |
@@ -14,6 +14,7 @@ import 'package:analyzer/src/dart/element/element.dart'; |
import 'package:analyzer/src/error/codes.dart'; |
import 'package:analyzer/src/generated/engine.dart'; |
import 'package:analyzer/src/generated/source_io.dart'; |
+import 'package:analyzer/src/task/strong/ast_properties.dart'; |
import 'package:front_end/src/base/errors.dart'; |
import 'package:test/test.dart'; |
import 'package:test_reflective_loader/test_reflective_loader.dart'; |
@@ -420,6 +421,249 @@ class StrongModeLocalInferenceTest extends ResolverTestCase { |
_isListOf(_isString)(exp.staticType); |
} |
+ test_covarianceChecks() async { |
+ var source = addSource(r''' |
+class C<T> { |
+ add(T t) {} |
+ forEach(void f(T t)) {} |
+} |
+class D extends C<int> { |
+ add(int t) {} |
+ forEach(void f(int t)) {} |
+} |
+class E extends C<int> { |
+ add(Object t) {} |
+ forEach(void f(Null t)) {} |
+} |
+'''); |
+ var unit = (await computeAnalysisResult(source)).unit; |
+ assertNoErrors(source); |
+ var cAdd = AstFinder.getMethodInClass(unit, "C", "add"); |
+ var covariantC = getClassCovariantParameters(AstFinder.getClass(unit, "C")); |
+ expect(covariantC.toList(), [cAdd.element.parameters[0]]); |
+ |
+ var dAdd = AstFinder.getMethodInClass(unit, "D", "add"); |
+ var covariantD = getClassCovariantParameters(AstFinder.getClass(unit, "D")); |
+ expect(covariantD.toList(), [dAdd.element.parameters[0]]); |
+ |
+ var covariantE = getClassCovariantParameters(AstFinder.getClass(unit, "E")); |
+ expect(covariantE.toList(), []); |
+ } |
+ |
+ test_covarianceChecks_genericMethods() async { |
+ var source = addSource(r''' |
+class C<T> { |
+ add<S>(T t) {} |
+ forEach<S>(S f(T t)) {} |
+} |
+class D extends C<int> { |
+ add<S>(int t) {} |
+ forEach<S>(S f(int t)) {} |
+} |
+class E extends C<int> { |
+ add<S>(Object t) {} |
+ forEach<S>(S f(Null t)) {} |
+} |
+'''); |
+ var unit = (await computeAnalysisResult(source)).unit; |
+ assertNoErrors(source); |
+ |
+ var cAdd = AstFinder.getMethodInClass(unit, "C", "add"); |
+ var covariantC = getClassCovariantParameters(AstFinder.getClass(unit, "C")); |
+ expect(covariantC.toList(), [cAdd.element.parameters[0]]); |
+ |
+ var dAdd = AstFinder.getMethodInClass(unit, "D", "add"); |
+ var covariantD = getClassCovariantParameters(AstFinder.getClass(unit, "D")); |
+ expect(covariantD.toList(), [dAdd.element.parameters[0]]); |
+ |
+ var covariantE = getClassCovariantParameters(AstFinder.getClass(unit, "E")); |
+ expect(covariantE.toList(), []); |
+ } |
+ |
+ test_covarianceChecks_superclass() async { |
+ var source = addSource(r''' |
+class C<T> { |
+ add(T t) {} |
+ forEach(void f(T t)) {} |
+} |
+class D { |
+ add(int t) {} |
+ forEach(void f(int t)) {} |
+} |
+class E extends D implements C<int> {} |
+'''); |
+ var unit = (await computeAnalysisResult(source)).unit; |
+ assertNoErrors(source); |
+ var cAdd = AstFinder.getMethodInClass(unit, "C", "add"); |
+ var covariantC = getClassCovariantParameters(AstFinder.getClass(unit, "C")); |
+ expect(covariantC.toList(), [cAdd.element.parameters[0]]); |
+ |
+ var dAdd = AstFinder.getMethodInClass(unit, "D", "add"); |
+ var covariantD = getClassCovariantParameters(AstFinder.getClass(unit, "D")); |
+ expect(covariantD, null); |
+ |
+ var classE = AstFinder.getClass(unit, "E"); |
+ var covariantE = getClassCovariantParameters(classE); |
+ var superCovariantE = getSuperclassCovariantParameters(classE); |
+ expect(covariantE.toList(), []); |
+ expect(superCovariantE.toList(), [dAdd.element.parameters[0]]); |
+ } |
+ |
+ @soloTest |
Leaf
2017/06/28 18:12:29
remove?
Jennifer Messerly
2017/07/05 20:11:20
Done.
|
+ test_covarianceChecks_returnFunction() async { |
+ var source = addSource(r''' |
+ |
+typedef F<T>(T t); |
+typedef T R<T>(); |
+class C<T> { |
+ F<T> f; |
+ F<T> get g => null; |
+ F<T> m1() => null; |
+ R<F<T>> m2() => null; |
+ |
+ casts(C<T> other, T t, F<T> fT, R<F<T>> rfT) { |
+ fT = f; |
+ fT = g; |
+ fT = m1(); |
+ rfT = m2(); |
+ |
+ f(t); |
+ g(t); |
+ (f)(t); |
+ (g)(t); |
+ m1()(t); |
+ m2()()(t); |
+ |
+ fT = this.f; |
+ fT = this.g; |
+ fT = this.m1(); |
+ rfT = this.m2(); |
+ |
+ this.f(t); |
+ this.g(t); |
+ (this.f)(t); |
+ (this.g)(t); |
+ this.m1()(t); |
+ this.m2()()(t); |
+ |
+ fT = other.f; |
+ fT = other.g; |
+ fT = other.m1(); |
+ rfT = other.m2(); |
+ |
+ other.f(t); |
+ other.g(t); |
+ (other.f)(t); |
+ (other.g)(t); |
+ other.m1()(t); |
+ other.m2()()(t); |
+ } |
+} |
+class D extends C<int> {} |
+ |
+D d; |
+C<Object> c; |
+F<Object> f; |
+F<Null> fN; |
+R<F<Object>> rf; |
+R<F<Null>> rfN; |
+R<R<F<Object>>> rrf; |
+R<R<F<Null>>> rrfN; |
+Object obj; |
+F<int> fi; |
+R<F<int>> rfi; |
+R<R<F<int>>> rrfi; |
+ |
+casts() { |
+ f = c.f; |
+ f = c.g; |
+ rf = c.m1; |
+ rrf = c.m2; |
+ f = c.m1(); |
+ rf = c.m2(); |
+ c.m2()(); |
+ |
+ c.f(obj); |
+ c.g(obj); |
+ (c.f)(obj); |
+ (c.g)(obj); |
+ (c.m1)(); |
+ c.m1()(obj); |
+ (c.m2)(); |
+} |
+ |
+noCasts() { |
+ c.f; |
+ c.g; |
+ c.m1; |
+ c.m1(); |
+ c.m2(); |
+ |
+ fN = c.f; |
+ fN = c.g; |
+ rfN = c.m1; |
+ rrfN = c.m2; |
+ fN = c.m1(); |
+ rfN = c.m2(); |
+ fN = c.m2()(); |
+ |
+ fi = d.f; |
+ fi = d.g; |
+ rfi = d.m1; |
+ fi = d.m1(); |
+ rrfi = d.m2; |
+ rfi = d.m2(); |
+ fi = d.m2()(); |
+ d.f(42); |
+ d.g(42); |
+ (d.f)(42); |
+ (d.g)(42); |
+ d.m1()(42); |
+ d.m2()()(42); |
+} |
+'''); |
+ var unit = (await computeAnalysisResult(source)).unit; |
+ assertNoErrors(source); |
+ |
+ void expectCast(Statement statement, bool hasCast) { |
+ var expr = (statement as ExpressionStatement).expression; |
+ Expression value; |
+ if (expr is AssignmentExpression) { |
+ value = expr.rightHandSide; |
+ } else { |
+ value = expr; |
+ while (value is InvocationExpression) { |
+ value = (value as InvocationExpression).function; |
+ } |
+ // We don't put the cast on the method itself |
+ if (value is SimpleIdentifier && value.name.startsWith('m')) { |
+ value = value.parent; |
+ } |
+ } |
+ while (value is ParenthesizedExpression) { |
+ value = (value as ParenthesizedExpression).expression; |
+ } |
+ var parent = value.parent; |
+ var cast = parent is MethodInvocation && value == parent.methodName |
+ ? getImplicitOperationCast(value) |
+ : getImplicitCast(value); |
+ expect(cast, hasCast ? isNotNull : isNull, |
+ reason: '`$expr` should ' + |
+ (hasCast ? '' : 'not ') + |
+ 'have a cast on `$value`.'); |
+ } |
+ |
+ for (var s in AstFinder.getStatementsInMethod(unit, 'C', 'casts')) { |
+ expectCast(s, true); |
+ } |
+ for (var s in AstFinder.getStatementsInTopLevelFunction(unit, 'noCasts')) { |
+ expectCast(s, false); |
+ } |
+ for (var s in AstFinder.getStatementsInTopLevelFunction(unit, 'casts')) { |
+ expectCast(s, true); |
+ } |
+ } |
+ |
test_factoryConstructor_propagation() async { |
String code = r''' |
class A<T> { |