Index: packages/analyzer/test/src/task/strong/checker_test.dart |
diff --git a/packages/analyzer/test/src/task/strong/checker_test.dart b/packages/analyzer/test/src/task/strong/checker_test.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..874916c36781679c278cdac76f752f343ef4cda6 |
--- /dev/null |
+++ b/packages/analyzer/test/src/task/strong/checker_test.dart |
@@ -0,0 +1,2311 @@ |
+// Copyright (c) 2015, 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. |
+ |
+// TODO(jmesserly): this file needs to be refactored, it's a port from |
+// package:dev_compiler's tests |
+/// General type checking tests |
+library test.src.task.strong.checker_test; |
+ |
+import 'package:unittest/unittest.dart'; |
+ |
+import 'strong_test_helper.dart'; |
+ |
+void main() { |
+ testChecker('ternary operator', { |
+ '/main.dart': ''' |
+ abstract class Comparable<T> { |
+ int compareTo(T other); |
+ static int compare(Comparable a, Comparable b) => a.compareTo(b); |
+ } |
+ typedef int Comparator<T>(T a, T b); |
+ |
+ typedef bool _Predicate<T>(T value); |
+ |
+ class SplayTreeMap<K, V> { |
+ Comparator<K> _comparator; |
+ _Predicate _validKey; |
+ |
+ // Initializing _comparator needs a cast, since K may not always be |
+ // Comparable. |
+ // Initializing _validKey shouldn't need a cast. Currently |
+ // it requires inference to work because of dartbug.com/23381 |
+ SplayTreeMap([int compare(K key1, K key2), |
+ bool isValidKey(potentialKey)]) { |
+ : _comparator = /*warning:DownCastComposite*/(compare == null) ? Comparable.compare : compare, |
+ _validKey = /*info:InferredType should be pass*/(isValidKey != null) ? isValidKey : ((v) => true); |
+ _Predicate<Object> _v = /*warning:DownCastComposite*/(isValidKey != null) ? isValidKey : ((v) => true); |
+ _v = /*info:InferredType should be pass*/(isValidKey != null) ? _v : ((v) => true); |
+ } |
+ } |
+ void main() { |
+ Object obj = 42; |
+ dynamic dyn = 42; |
+ int i = 42; |
+ |
+ // Check the boolean conversion of the condition. |
+ print((/*severe:StaticTypeError*/i) ? false : true); |
+ print((/*info:DownCastImplicit*/obj) ? false : true); |
+ print((/*info:DynamicCast*/dyn) ? false : true); |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('if/for/do/while statements use boolean conversion', { |
+ '/main.dart': ''' |
+ main() { |
+ dynamic d = 42; |
+ Object obj = 42; |
+ int i = 42; |
+ bool b = false; |
+ |
+ if (b) {} |
+ if (/*info:DynamicCast*/dyn) {} |
+ if (/*info:DownCastImplicit*/obj) {} |
+ if (/*severe:StaticTypeError*/i) {} |
+ |
+ while (b) {} |
+ while (/*info:DynamicCast*/dyn) {} |
+ while (/*info:DownCastImplicit*/obj) {} |
+ while (/*severe:StaticTypeError*/i) {} |
+ |
+ do {} while (b); |
+ do {} while (/*info:DynamicCast*/dyn); |
+ do {} while (/*info:DownCastImplicit*/obj); |
+ do {} while (/*severe:StaticTypeError*/i); |
+ |
+ for (;b;) {} |
+ for (;/*info:DynamicCast*/dyn;) {} |
+ for (;/*info:DownCastImplicit*/obj;) {} |
+ for (;/*severe:StaticTypeError*/i;) {} |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('dynamic invocation', { |
+ '/main.dart': ''' |
+ |
+ class A { |
+ dynamic call(dynamic x) => x; |
+ } |
+ class B extends A { |
+ int call(int x) => x; |
+ double col(double x) => x; |
+ } |
+ void main() { |
+ { |
+ B f = new B(); |
+ int x; |
+ double y; |
+ // The analyzer has what I believe is a bug (dartbug.com/23252) which |
+ // causes the return type of calls to f to be treated as dynamic. |
+ x = /*info:DynamicCast should be pass*/f(3); |
+ x = /*severe:StaticTypeError*/f.col(3.0); |
+ y = /*info:DynamicCast should be severe:StaticTypeError*/f(3); |
+ y = f.col(3.0); |
+ f(/*severe:StaticTypeError*/3.0); |
+ f.col(/*severe:StaticTypeError*/3); |
+ } |
+ { |
+ Function f = new B(); |
+ int x; |
+ double y; |
+ x = /*info:DynamicCast, info:DynamicInvoke*/f(3); |
+ x = /*info:DynamicCast, info:DynamicInvoke*/f.col(3.0); |
+ y = /*info:DynamicCast, info:DynamicInvoke*/f(3); |
+ y = /*info:DynamicCast, info:DynamicInvoke*/f.col(3.0); |
+ (/*info:DynamicInvoke*/f(3.0)); |
+ (/*info:DynamicInvoke*/f.col(3)); |
+ } |
+ { |
+ A f = new B(); |
+ int x; |
+ double y; |
+ x = /*info:DynamicCast, info:DynamicInvoke*/f(3); |
+ y = /*info:DynamicCast, info:DynamicInvoke*/f(3); |
+ (/*info:DynamicInvoke*/f(3.0)); |
+ } |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('conversion and dynamic invoke', { |
+ '/helper.dart': ''' |
+ dynamic toString = (int x) => x + 42; |
+ dynamic hashCode = "hello"; |
+ ''', |
+ '/main.dart': ''' |
+ import 'helper.dart' as helper; |
+ |
+ class A { |
+ String x = "hello world"; |
+ |
+ void baz1(y) => x + /*info:DynamicCast*/y; |
+ static baz2(y) => /*info:DynamicInvoke*/y + y; |
+ } |
+ |
+ void foo(String str) { |
+ print(str); |
+ } |
+ |
+ class B { |
+ String toString([int arg]) => arg.toString(); |
+ } |
+ |
+ void bar(a) { |
+ foo(/*info:DynamicCast,info:DynamicInvoke*/a.x); |
+ } |
+ |
+ baz() => new B(); |
+ |
+ typedef DynFun(x); |
+ typedef StrFun(String x); |
+ |
+ var bar1 = bar; |
+ |
+ void main() { |
+ var a = new A(); |
+ bar(a); |
+ (/*info:DynamicInvoke*/bar1(a)); |
+ var b = bar; |
+ (/*info:DynamicInvoke*/b(a)); |
+ var f1 = foo; |
+ f1("hello"); |
+ dynamic f2 = foo; |
+ (/*info:DynamicInvoke*/f2("hello")); |
+ DynFun f3 = foo; |
+ (/*info:DynamicInvoke*/f3("hello")); |
+ (/*info:DynamicInvoke*/f3(42)); |
+ StrFun f4 = foo; |
+ f4("hello"); |
+ a.baz1("hello"); |
+ var b1 = a.baz1; |
+ (/*info:DynamicInvoke*/b1("hello")); |
+ A.baz2("hello"); |
+ var b2 = A.baz2; |
+ (/*info:DynamicInvoke*/b2("hello")); |
+ |
+ dynamic a1 = new B(); |
+ (/*info:DynamicInvoke*/a1.x); |
+ a1.toString(); |
+ (/*info:DynamicInvoke*/a1.toString(42)); |
+ var toStringClosure = a1.toString; |
+ (/*info:DynamicInvoke*/a1.toStringClosure()); |
+ (/*info:DynamicInvoke*/a1.toStringClosure(42)); |
+ (/*info:DynamicInvoke*/a1.toStringClosure("hello")); |
+ a1.hashCode; |
+ |
+ dynamic toString = () => null; |
+ (/*info:DynamicInvoke*/toString()); |
+ |
+ (/*info:DynamicInvoke*/helper.toString()); |
+ var toStringClosure2 = helper.toString; |
+ (/*info:DynamicInvoke*/toStringClosure2()); |
+ int hashCode = /*info:DynamicCast*/helper.hashCode; |
+ |
+ baz().toString(); |
+ baz().hashCode; |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Constructors', { |
+ '/main.dart': ''' |
+ const num z = 25; |
+ Object obj = "world"; |
+ |
+ class A { |
+ int x; |
+ String y; |
+ |
+ A(this.x) : this.y = /*severe:StaticTypeError*/42; |
+ |
+ A.c1(p): this.x = /*info:DownCastImplicit*/z, this.y = /*info:DynamicCast*/p; |
+ |
+ A.c2(this.x, this.y); |
+ |
+ A.c3(/*severe:InvalidParameterDeclaration*/num this.x, String this.y); |
+ } |
+ |
+ class B extends A { |
+ B() : super(/*severe:StaticTypeError*/"hello"); |
+ |
+ B.c2(int x, String y) : super.c2(/*severe:StaticTypeError*/y, |
+ /*severe:StaticTypeError*/x); |
+ |
+ B.c3(num x, Object y) : super.c3(x, /*info:DownCastImplicit*/y); |
+ } |
+ |
+ void main() { |
+ A a = new A.c2(/*info:DownCastImplicit*/z, /*severe:StaticTypeError*/z); |
+ var b = new B.c2(/*severe:StaticTypeError*/"hello", /*info:DownCastImplicit*/obj); |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Unbound variable', { |
+ '/main.dart': ''' |
+ void main() { |
+ dynamic y = /*pass should be severe:StaticTypeError*/unboundVariable; |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Unbound type name', { |
+ '/main.dart': ''' |
+ void main() { |
+ /*pass should be severe:StaticTypeError*/AToB y; |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Ground type subtyping: dynamic is top', { |
+ '/main.dart': ''' |
+ |
+ class A {} |
+ class B extends A {} |
+ |
+ void main() { |
+ dynamic y; |
+ Object o; |
+ int i = 0; |
+ double d = 0.0; |
+ num n; |
+ A a; |
+ B b; |
+ y = o; |
+ y = i; |
+ y = d; |
+ y = n; |
+ y = a; |
+ y = b; |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Ground type subtyping: dynamic downcasts', { |
+ '/main.dart': ''' |
+ |
+ class A {} |
+ class B extends A {} |
+ |
+ void main() { |
+ dynamic y; |
+ Object o; |
+ int i = 0; |
+ double d = 0.0; |
+ num n; |
+ A a; |
+ B b; |
+ o = y; |
+ i = /*info:DynamicCast*/y; |
+ d = /*info:DynamicCast*/y; |
+ n = /*info:DynamicCast*/y; |
+ a = /*info:DynamicCast*/y; |
+ b = /*info:DynamicCast*/y; |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Ground type subtyping: assigning a class', { |
+ '/main.dart': ''' |
+ |
+ class A {} |
+ class B extends A {} |
+ |
+ void main() { |
+ dynamic y; |
+ Object o; |
+ int i = 0; |
+ double d = 0.0; |
+ num n; |
+ A a; |
+ B b; |
+ y = a; |
+ o = a; |
+ i = /*severe:StaticTypeError*/a; |
+ d = /*severe:StaticTypeError*/a; |
+ n = /*severe:StaticTypeError*/a; |
+ a = a; |
+ b = /*info:DownCastImplicit*/a; |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Ground type subtyping: assigning a subclass', { |
+ '/main.dart': ''' |
+ |
+ class A {} |
+ class B extends A {} |
+ class C extends A {} |
+ |
+ void main() { |
+ dynamic y; |
+ Object o; |
+ int i = 0; |
+ double d = 0.0; |
+ num n; |
+ A a; |
+ B b; |
+ C c; |
+ y = b; |
+ o = b; |
+ i = /*severe:StaticTypeError*/b; |
+ d = /*severe:StaticTypeError*/b; |
+ n = /*severe:StaticTypeError*/b; |
+ a = b; |
+ b = b; |
+ c = /*severe:StaticTypeError*/b; |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Ground type subtyping: interfaces', { |
+ '/main.dart': ''' |
+ |
+ class A {} |
+ class B extends A {} |
+ class C extends A {} |
+ class D extends B implements C {} |
+ |
+ void main() { |
+ A top; |
+ B left; |
+ C right; |
+ D bot; |
+ { |
+ top = top; |
+ top = left; |
+ top = right; |
+ top = bot; |
+ } |
+ { |
+ left = /*info:DownCastImplicit*/top; |
+ left = left; |
+ left = /*severe:StaticTypeError*/right; |
+ left = bot; |
+ } |
+ { |
+ right = /*info:DownCastImplicit*/top; |
+ right = /*severe:StaticTypeError*/left; |
+ right = right; |
+ right = bot; |
+ } |
+ { |
+ bot = /*info:DownCastImplicit*/top; |
+ bot = /*info:DownCastImplicit*/left; |
+ bot = /*info:DownCastImplicit*/right; |
+ bot = bot; |
+ } |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Function typing and subtyping: int and object', { |
+ '/main.dart': ''' |
+ |
+ typedef Object Top(int x); // Top of the lattice |
+ typedef int Left(int x); // Left branch |
+ typedef int Left2(int x); // Left branch |
+ typedef Object Right(Object x); // Right branch |
+ typedef int Bot(Object x); // Bottom of the lattice |
+ |
+ Object top(int x) => x; |
+ int left(int x) => x; |
+ Object right(Object x) => x; |
+ int _bot(Object x) => /*info:DownCastImplicit*/x; |
+ int bot(Object x) => x as int; |
+ |
+ void main() { |
+ { // Check typedef equality |
+ Left f = left; |
+ Left2 g = f; |
+ } |
+ { |
+ Top f; |
+ f = top; |
+ f = left; |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Left f; |
+ f = /*warning:DownCastComposite*/top; |
+ f = left; |
+ f = /*warning:DownCastComposite*/right; // Should we reject this? |
+ f = bot; |
+ } |
+ { |
+ Right f; |
+ f = /*warning:DownCastComposite*/top; |
+ f = /*warning:DownCastComposite*/left; // Should we reject this? |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Bot f; |
+ f = /*warning:DownCastComposite*/top; |
+ f = /*warning:DownCastComposite*/left; |
+ f = /*warning:DownCastComposite*/right; |
+ f = bot; |
+ } |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Function typing and subtyping: classes', { |
+ '/main.dart': ''' |
+ |
+ class A {} |
+ class B extends A {} |
+ |
+ typedef A Top(B x); // Top of the lattice |
+ typedef B Left(B x); // Left branch |
+ typedef B Left2(B x); // Left branch |
+ typedef A Right(A x); // Right branch |
+ typedef B Bot(A x); // Bottom of the lattice |
+ |
+ B left(B x) => x; |
+ B _bot(A x) => /*info:DownCastImplicit*/x; |
+ B bot(A x) => x as B; |
+ A top(B x) => x; |
+ A right(A x) => x; |
+ |
+ void main() { |
+ { // Check typedef equality |
+ Left f = left; |
+ Left2 g = f; |
+ } |
+ { |
+ Top f; |
+ f = top; |
+ f = left; |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Left f; |
+ f = /*warning:DownCastComposite*/top; |
+ f = left; |
+ f = /*warning:DownCastComposite*/right; // Should we reject this? |
+ f = bot; |
+ } |
+ { |
+ Right f; |
+ f = /*warning:DownCastComposite*/top; |
+ f = /*warning:DownCastComposite*/left; // Should we reject this? |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Bot f; |
+ f = /*warning:DownCastComposite*/top; |
+ f = /*warning:DownCastComposite*/left; |
+ f = /*warning:DownCastComposite*/right; |
+ f = bot; |
+ } |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Function typing and subtyping: dynamic', { |
+ '/main.dart': ''' |
+ |
+ class A {} |
+ |
+ typedef dynamic Top(dynamic x); // Top of the lattice |
+ typedef dynamic Left(A x); // Left branch |
+ typedef A Right(dynamic x); // Right branch |
+ typedef A Bottom(A x); // Bottom of the lattice |
+ |
+ dynamic left(A x) => x; |
+ A bot(A x) => x; |
+ dynamic top(dynamic x) => x; |
+ A right(dynamic x) => /*info:DynamicCast*/x; |
+ |
+ void main() { |
+ { |
+ Top f; |
+ f = top; |
+ f = left; |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Left f; |
+ f = /*warning:DownCastComposite*/top; |
+ f = left; |
+ f = /*warning:DownCastComposite*/right; |
+ f = bot; |
+ } |
+ { |
+ Right f; |
+ f = /*warning:DownCastComposite*/top; |
+ f = /*warning:DownCastComposite*/left; |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Bottom f; |
+ f = /*warning:DownCastComposite*/top; |
+ f = /*warning:DownCastComposite*/left; |
+ f = /*warning:DownCastComposite*/right; |
+ f = bot; |
+ } |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Function typing and subtyping: function literal variance', { |
+ '/main.dart': ''' |
+ |
+ class A {} |
+ class B extends A {} |
+ |
+ typedef T Function2<S, T>(S z); |
+ |
+ A top(B x) => x; |
+ B left(B x) => x; |
+ A right(A x) => x; |
+ B bot(A x) => x as B; |
+ |
+ void main() { |
+ { |
+ Function2<B, A> f; |
+ f = top; |
+ f = left; |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Function2<B, B> f; |
+ f = /*warning:DownCastComposite*/top; |
+ f = left; |
+ f = /*warning:DownCastComposite*/right; // Should we reject this? |
+ f = bot; |
+ } |
+ { |
+ Function2<A, A> f; |
+ f = /*warning:DownCastComposite*/top; |
+ f = /*warning:DownCastComposite*/left; // Should we reject this? |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Function2<A, B> f; |
+ f = /*warning:DownCastComposite*/top; |
+ f = /*warning:DownCastComposite*/left; |
+ f = /*warning:DownCastComposite*/right; |
+ f = bot; |
+ } |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Function typing and subtyping: function variable variance', { |
+ '/main.dart': ''' |
+ |
+ class A {} |
+ class B extends A {} |
+ |
+ typedef T Function2<S, T>(S z); |
+ |
+ void main() { |
+ { |
+ Function2<B, A> top; |
+ Function2<B, B> left; |
+ Function2<A, A> right; |
+ Function2<A, B> bot; |
+ |
+ top = right; |
+ top = bot; |
+ top = top; |
+ top = left; |
+ |
+ left = /*warning:DownCastComposite*/top; |
+ left = left; |
+ left = /*warning:DownCastComposite*/right; // Should we reject this? |
+ left = bot; |
+ |
+ right = /*warning:DownCastComposite*/top; |
+ right = /*warning:DownCastComposite*/left; // Should we reject this? |
+ right = right; |
+ right = bot; |
+ |
+ bot = /*warning:DownCastComposite*/top; |
+ bot = /*warning:DownCastComposite*/left; |
+ bot = /*warning:DownCastComposite*/right; |
+ bot = bot; |
+ } |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Function typing and subtyping: higher order function literals', { |
+ '/main.dart': ''' |
+ |
+ class A {} |
+ class B extends A {} |
+ |
+ typedef T Function2<S, T>(S z); |
+ |
+ typedef A BToA(B x); // Top of the base lattice |
+ typedef B AToB(A x); // Bot of the base lattice |
+ |
+ BToA top(AToB f) => f; |
+ AToB left(AToB f) => f; |
+ BToA right(BToA f) => f; |
+ AToB _bot(BToA f) => /*warning:DownCastComposite*/f; |
+ AToB bot(BToA f) => f as AToB; |
+ |
+ Function2<B, A> top(AToB f) => f; |
+ Function2<A, B> left(AToB f) => f; |
+ Function2<B, A> right(BToA f) => f; |
+ Function2<A, B> _bot(BToA f) => /*warning:DownCastComposite*/f; |
+ Function2<A, B> bot(BToA f) => f as Function2<A, B>; |
+ |
+ |
+ BToA top(Function2<A, B> f) => f; |
+ AToB left(Function2<A, B> f) => f; |
+ BToA right(Function2<B, A> f) => f; |
+ AToB _bot(Function2<B, A> f) => /*warning:DownCastComposite*/f; |
+ AToB bot(Function2<B, A> f) => f as AToB; |
+ |
+ void main() { |
+ { |
+ Function2<AToB, BToA> f; // Top |
+ f = top; |
+ f = left; |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Function2<AToB, AToB> f; // Left |
+ f = /*warning:DownCastComposite*/top; |
+ f = left; |
+ f = /*warning:DownCastComposite*/right; // Should we reject this? |
+ f = bot; |
+ } |
+ { |
+ Function2<BToA, BToA> f; // Right |
+ f = /*warning:DownCastComposite*/top; |
+ f = /*warning:DownCastComposite*/left; // Should we reject this? |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Function2<BToA, AToB> f; // Bot |
+ f = bot; |
+ f = /*warning:DownCastComposite*/left; |
+ f = /*warning:DownCastComposite*/top; |
+ f = /*warning:DownCastComposite*/left; |
+ } |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker( |
+ 'Function typing and subtyping: higher order function variables', { |
+ '/main.dart': ''' |
+ |
+ class A {} |
+ class B extends A {} |
+ |
+ typedef T Function2<S, T>(S z); |
+ |
+ void main() { |
+ { |
+ Function2<Function2<A, B>, Function2<B, A>> top; |
+ Function2<Function2<B, A>, Function2<B, A>> right; |
+ Function2<Function2<A, B>, Function2<A, B>> left; |
+ Function2<Function2<B, A>, Function2<A, B>> bot; |
+ |
+ top = right; |
+ top = bot; |
+ top = top; |
+ top = left; |
+ |
+ left = /*warning:DownCastComposite*/top; |
+ left = left; |
+ left = |
+ /*warning:DownCastComposite should be severe:StaticTypeError*/right; |
+ left = bot; |
+ |
+ right = /*warning:DownCastComposite*/top; |
+ right = |
+ /*warning:DownCastComposite should be severe:StaticTypeError*/left; |
+ right = right; |
+ right = bot; |
+ |
+ bot = /*warning:DownCastComposite*/top; |
+ bot = /*warning:DownCastComposite*/left; |
+ bot = /*warning:DownCastComposite*/right; |
+ bot = bot; |
+ } |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Function typing and subtyping: named and optional parameters', { |
+ '/main.dart': ''' |
+ |
+ class A {} |
+ |
+ typedef A FR(A x); |
+ typedef A FO([A x]); |
+ typedef A FN({A x}); |
+ typedef A FRR(A x, A y); |
+ typedef A FRO(A x, [A y]); |
+ typedef A FRN(A x, {A n}); |
+ typedef A FOO([A x, A y]); |
+ typedef A FNN({A x, A y}); |
+ typedef A FNNN({A z, A y, A x}); |
+ |
+ void main() { |
+ FR r; |
+ FO o; |
+ FN n; |
+ FRR rr; |
+ FRO ro; |
+ FRN rn; |
+ FOO oo; |
+ FNN nn; |
+ FNNN nnn; |
+ |
+ r = r; |
+ r = o; |
+ r = /*severe:StaticTypeError*/n; |
+ r = /*severe:StaticTypeError*/rr; |
+ r = ro; |
+ r = rn; |
+ r = oo; |
+ r = /*severe:StaticTypeError*/nn; |
+ r = /*severe:StaticTypeError*/nnn; |
+ |
+ o = /*warning:DownCastComposite*/r; |
+ o = o; |
+ o = /*severe:StaticTypeError*/n; |
+ o = /*severe:StaticTypeError*/rr; |
+ o = /*severe:StaticTypeError*/ro; |
+ o = /*severe:StaticTypeError*/rn; |
+ o = oo; |
+ o = /*severe:StaticTypeError*/nn |
+ o = /*severe:StaticTypeError*/nnn; |
+ |
+ n = /*severe:StaticTypeError*/r; |
+ n = /*severe:StaticTypeError*/o; |
+ n = n; |
+ n = /*severe:StaticTypeError*/rr; |
+ n = /*severe:StaticTypeError*/ro; |
+ n = /*severe:StaticTypeError*/rn; |
+ n = /*severe:StaticTypeError*/oo; |
+ n = nn; |
+ n = nnn; |
+ |
+ rr = /*severe:StaticTypeError*/r; |
+ rr = /*severe:StaticTypeError*/o; |
+ rr = /*severe:StaticTypeError*/n; |
+ rr = rr; |
+ rr = ro; |
+ rr = /*severe:StaticTypeError*/rn; |
+ rr = oo; |
+ rr = /*severe:StaticTypeError*/nn; |
+ rr = /*severe:StaticTypeError*/nnn; |
+ |
+ ro = /*warning:DownCastComposite*/r; |
+ ro = /*severe:StaticTypeError*/o; |
+ ro = /*severe:StaticTypeError*/n; |
+ ro = /*warning:DownCastComposite*/rr; |
+ ro = ro; |
+ ro = /*severe:StaticTypeError*/rn; |
+ ro = oo; |
+ ro = /*severe:StaticTypeError*/nn; |
+ ro = /*severe:StaticTypeError*/nnn; |
+ |
+ rn = /*warning:DownCastComposite*/r; |
+ rn = /*severe:StaticTypeError*/o; |
+ rn = /*severe:StaticTypeError*/n; |
+ rn = /*severe:StaticTypeError*/rr; |
+ rn = /*severe:StaticTypeError*/ro; |
+ rn = rn; |
+ rn = /*severe:StaticTypeError*/oo; |
+ rn = /*severe:StaticTypeError*/nn; |
+ rn = /*severe:StaticTypeError*/nnn; |
+ |
+ oo = /*warning:DownCastComposite*/r; |
+ oo = /*warning:DownCastComposite*/o; |
+ oo = /*severe:StaticTypeError*/n; |
+ oo = /*warning:DownCastComposite*/rr; |
+ oo = /*warning:DownCastComposite*/ro; |
+ oo = /*severe:StaticTypeError*/rn; |
+ oo = oo; |
+ oo = /*severe:StaticTypeError*/nn; |
+ oo = /*severe:StaticTypeError*/nnn; |
+ |
+ nn = /*severe:StaticTypeError*/r; |
+ nn = /*severe:StaticTypeError*/o; |
+ nn = /*warning:DownCastComposite*/n; |
+ nn = /*severe:StaticTypeError*/rr; |
+ nn = /*severe:StaticTypeError*/ro; |
+ nn = /*severe:StaticTypeError*/rn; |
+ nn = /*severe:StaticTypeError*/oo; |
+ nn = nn; |
+ nn = nnn; |
+ |
+ nnn = /*severe:StaticTypeError*/r; |
+ nnn = /*severe:StaticTypeError*/o; |
+ nnn = /*warning:DownCastComposite*/n; |
+ nnn = /*severe:StaticTypeError*/rr; |
+ nnn = /*severe:StaticTypeError*/ro; |
+ nnn = /*severe:StaticTypeError*/rn; |
+ nnn = /*severe:StaticTypeError*/oo; |
+ nnn = /*warning:DownCastComposite*/nn; |
+ nnn = nnn; |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Function subtyping: objects with call methods', { |
+ '/main.dart': ''' |
+ |
+ typedef int I2I(int x); |
+ typedef num N2N(num x); |
+ class A { |
+ int call(int x) => x; |
+ } |
+ class B { |
+ num call(num x) => x; |
+ } |
+ int i2i(int x) => x; |
+ num n2n(num x) => x; |
+ void main() { |
+ { |
+ I2I f; |
+ f = new A(); |
+ f = /*severe:StaticTypeError*/new B(); |
+ f = i2i; |
+ f = /*warning:DownCastComposite*/n2n; |
+ f = /*warning:DownCastComposite*/i2i as Object; |
+ f = /*warning:DownCastComposite*/n2n as Function; |
+ } |
+ { |
+ N2N f; |
+ f = /*severe:StaticTypeError*/new A(); |
+ f = new B(); |
+ f = /*warning:DownCastComposite*/i2i; |
+ f = n2n; |
+ f = /*warning:DownCastComposite*/i2i as Object; |
+ f = /*warning:DownCastComposite*/n2n as Function; |
+ } |
+ { |
+ A f; |
+ f = new A(); |
+ f = /*severe:StaticTypeError*/new B(); |
+ f = /*severe:StaticTypeError*/i2i; |
+ f = /*severe:StaticTypeError*/n2n; |
+ f = /*info:DownCastImplicit*/i2i as Object; |
+ f = /*info:DownCastImplicit*/n2n as Function; |
+ } |
+ { |
+ B f; |
+ f = /*severe:StaticTypeError*/new A(); |
+ f = new B(); |
+ f = /*severe:StaticTypeError*/i2i; |
+ f = /*severe:StaticTypeError*/n2n; |
+ f = /*info:DownCastImplicit*/i2i as Object; |
+ f = /*info:DownCastImplicit*/n2n as Function; |
+ } |
+ { |
+ Function f; |
+ f = new A(); |
+ f = new B(); |
+ f = i2i; |
+ f = n2n; |
+ f = /*info:DownCastImplicit*/i2i as Object; |
+ f = (n2n as Function); |
+ } |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Function typing and subtyping: void', { |
+ '/main.dart': ''' |
+ |
+ class A { |
+ void bar() => null; |
+ void foo() => bar; // allowed |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Relaxed casts', { |
+ '/main.dart': ''' |
+ |
+ class A {} |
+ |
+ class L<T> {} |
+ class M<T> extends L<T> {} |
+ // L<dynamic|Object> |
+ // / \ |
+ // M<dynamic|Object> L<A> |
+ // \ / |
+ // M<A> |
+ // In normal Dart, there are additional edges |
+ // from M<A> to M<dynamic> |
+ // from L<A> to M<dynamic> |
+ // from L<A> to L<dynamic> |
+ void main() { |
+ L lOfDs; |
+ L<Object> lOfOs; |
+ L<A> lOfAs; |
+ |
+ M mOfDs; |
+ M<Object> mOfOs; |
+ M<A> mOfAs; |
+ |
+ { |
+ lOfDs = mOfDs; |
+ lOfDs = mOfOs; |
+ lOfDs = mOfAs; |
+ lOfDs = lOfDs; |
+ lOfDs = lOfOs; |
+ lOfDs = lOfAs; |
+ } |
+ { |
+ lOfOs = mOfDs; |
+ lOfOs = mOfOs; |
+ lOfOs = mOfAs; |
+ lOfOs = lOfDs; |
+ lOfOs = lOfOs; |
+ lOfOs = lOfAs; |
+ } |
+ { |
+ lOfAs = /*warning:DownCastComposite*/mOfDs; |
+ lOfAs = /*severe:StaticTypeError*/mOfOs; |
+ lOfAs = mOfAs; |
+ lOfAs = /*warning:DownCastComposite*/lOfDs; |
+ lOfAs = /*warning:DownCastComposite*/lOfOs; |
+ lOfAs = lOfAs; |
+ } |
+ { |
+ mOfDs = mOfDs; |
+ mOfDs = mOfOs; |
+ mOfDs = mOfAs; |
+ mOfDs = /*info:DownCastImplicit*/lOfDs; |
+ mOfDs = /*info:DownCastImplicit*/lOfOs; |
+ mOfDs = /*info:DownCastImplicit*/lOfAs; |
+ } |
+ { |
+ mOfOs = mOfDs; |
+ mOfOs = mOfOs; |
+ mOfOs = mOfAs; |
+ mOfOs = /*info:DownCastImplicit*/lOfDs; |
+ mOfOs = /*info:DownCastImplicit*/lOfOs; |
+ mOfOs = /*severe:StaticTypeError*/lOfAs; |
+ } |
+ { |
+ mOfAs = /*warning:DownCastComposite*/mOfDs; |
+ mOfAs = /*warning:DownCastComposite*/mOfOs; |
+ mOfAs = mOfAs; |
+ mOfAs = /*warning:DownCastComposite*/lOfDs; |
+ mOfAs = /*warning:DownCastComposite*/lOfOs; |
+ mOfAs = /*warning:DownCastComposite*/lOfAs; |
+ } |
+ |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('Type checking literals', { |
+ '/main.dart': ''' |
+ test() { |
+ num n = 3; |
+ int i = 3; |
+ String s = "hello"; |
+ { |
+ List<int> l = <int>[i]; |
+ l = <int>[/*severe:StaticTypeError*/s]; |
+ l = <int>[/*info:DownCastImplicit*/n]; |
+ l = <int>[i, /*info:DownCastImplicit*/n, /*severe:StaticTypeError*/s]; |
+ } |
+ { |
+ List l = [i]; |
+ l = [s]; |
+ l = [n]; |
+ l = [i, n, s]; |
+ } |
+ { |
+ Map<String, int> m = <String, int>{s: i}; |
+ m = <String, int>{s: /*severe:StaticTypeError*/s}; |
+ m = <String, int>{s: /*info:DownCastImplicit*/n}; |
+ m = <String, int>{s: i, |
+ s: /*info:DownCastImplicit*/n, |
+ s: /*severe:StaticTypeError*/s}; |
+ } |
+ // TODO(leafp): We can't currently test for key errors since the |
+ // error marker binds to the entire entry. |
+ { |
+ Map m = {s: i}; |
+ m = {s: s}; |
+ m = {s: n}; |
+ m = {s: i, |
+ s: n, |
+ s: s}; |
+ m = {i: s, |
+ n: s, |
+ s: s}; |
+ } |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('casts in constant contexts', { |
+ '/main.dart': ''' |
+ class A { |
+ static const num n = 3.0; |
+ static const int i = /*info:AssignmentCast*/n; |
+ final int fi; |
+ const A(num a) : this.fi = /*info:DownCastImplicit*/a; |
+ } |
+ class B extends A { |
+ const B(Object a) : super(/*info:DownCastImplicit*/a); |
+ } |
+ void foo(Object o) { |
+ var a = const A(/*info:DownCastImplicit*/o); |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('casts in conditionals', { |
+ '/main.dart': ''' |
+ main() { |
+ bool b = true; |
+ num x = b ? 1 : 2.3; |
+ int y = /*info:AssignmentCast*/b ? 1 : 2.3; |
+ String z = !b ? "hello" : null; |
+ z = b ? null : "hello"; |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('redirecting constructor', { |
+ '/main.dart': ''' |
+ class A { |
+ A(A x) {} |
+ A.two() : this(/*severe:StaticTypeError*/3); |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('super constructor', { |
+ '/main.dart': ''' |
+ class A { A(A x) {} } |
+ class B extends A { |
+ B() : super(/*severe:StaticTypeError*/3); |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('field/field override', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B extends A {} |
+ class C extends B {} |
+ |
+ class Base { |
+ B f1; |
+ B f2; |
+ B f3; |
+ B f4; |
+ } |
+ |
+ class Child extends Base { |
+ /*severe:InvalidMethodOverride*/A f1; // invalid for getter |
+ /*severe:InvalidMethodOverride*/C f2; // invalid for setter |
+ var f3; |
+ /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/dynamic f4; |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('getter/getter override', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B extends A {} |
+ class C extends B {} |
+ |
+ abstract class Base { |
+ B get f1; |
+ B get f2; |
+ B get f3; |
+ B get f4; |
+ } |
+ |
+ class Child extends Base { |
+ /*severe:InvalidMethodOverride*/A get f1 => null; |
+ C get f2 => null; |
+ get f3 => null; |
+ /*severe:InvalidMethodOverride*/dynamic get f4 => null; |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('field/getter override', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B extends A {} |
+ class C extends B {} |
+ |
+ abstract class Base { |
+ B f1; |
+ B f2; |
+ B f3; |
+ B f4; |
+ } |
+ |
+ class Child extends Base { |
+ /*severe:InvalidMethodOverride*/A get f1 => null; |
+ C get f2 => null; |
+ get f3 => null; |
+ /*severe:InvalidMethodOverride*/dynamic get f4 => null; |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('setter/setter override', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B extends A {} |
+ class C extends B {} |
+ |
+ abstract class Base { |
+ void set f1(B value); |
+ void set f2(B value); |
+ void set f3(B value); |
+ void set f4(B value); |
+ void set f5(B value); |
+ } |
+ |
+ class Child extends Base { |
+ void set f1(A value) {} |
+ /*severe:InvalidMethodOverride*/void set f2(C value) {} |
+ void set f3(value) {} |
+ /*severe:InvalidMethodOverride*/void set f4(dynamic value) {} |
+ set f5(B value) {} |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('field/setter override', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B extends A {} |
+ class C extends B {} |
+ |
+ class Base { |
+ B f1; |
+ B f2; |
+ B f3; |
+ B f4; |
+ B f5; |
+ } |
+ |
+ class Child extends Base { |
+ B get f1 => null; |
+ B get f2 => null; |
+ B get f3 => null; |
+ B get f4 => null; |
+ B get f5 => null; |
+ |
+ void set f1(A value) {} |
+ /*severe:InvalidMethodOverride*/void set f2(C value) {} |
+ void set f3(value) {} |
+ /*severe:InvalidMethodOverride*/void set f4(dynamic value) {} |
+ set f5(B value) {} |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('method override', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B extends A {} |
+ class C extends B {} |
+ |
+ class Base { |
+ B m1(B a); |
+ B m2(B a); |
+ B m3(B a); |
+ B m4(B a); |
+ B m5(B a); |
+ B m6(B a); |
+ } |
+ |
+ class Child extends Base { |
+ /*severe:InvalidMethodOverride*/A m1(A value) {} |
+ /*severe:InvalidMethodOverride*/C m2(C value) {} |
+ /*severe:InvalidMethodOverride*/A m3(C value) {} |
+ C m4(A value) {} |
+ m5(value) {} |
+ /*severe:InvalidMethodOverride*/dynamic m6(dynamic value) {} |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('unary operators', { |
+ '/main.dart': ''' |
+ class A { |
+ A operator ~() {} |
+ A operator +(int x) {} |
+ A operator -(int x) {} |
+ A operator -() {} |
+ } |
+ |
+ foo() => new A(); |
+ |
+ test() { |
+ A a = new A(); |
+ var c = foo(); |
+ |
+ ~a; |
+ (/*info:DynamicInvoke*/~d); |
+ |
+ !/*severe:StaticTypeError*/a; |
+ !/*info:DynamicCast*/d; |
+ |
+ -a; |
+ (/*info:DynamicInvoke*/-d); |
+ |
+ ++a; |
+ --a; |
+ (/*info:DynamicInvoke*/++d); |
+ (/*info:DynamicInvoke*/--d); |
+ |
+ a++; |
+ a--; |
+ (/*info:DynamicInvoke*/d++); |
+ (/*info:DynamicInvoke*/d--); |
+ }''' |
+ }); |
+ |
+ testChecker('binary and index operators', { |
+ '/main.dart': ''' |
+ class A { |
+ A operator *(B b) {} |
+ A operator /(B b) {} |
+ A operator ~/(B b) {} |
+ A operator %(B b) {} |
+ A operator +(B b) {} |
+ A operator -(B b) {} |
+ A operator <<(B b) {} |
+ A operator >>(B b) {} |
+ A operator &(B b) {} |
+ A operator ^(B b) {} |
+ A operator |(B b) {} |
+ A operator[](B b) {} |
+ } |
+ |
+ class B { |
+ A operator -(B b) {} |
+ } |
+ |
+ foo() => new A(); |
+ |
+ test() { |
+ A a = new A(); |
+ B b = new B(); |
+ var c = foo(); |
+ a = a * b; |
+ a = a * /*info:DynamicCast*/c; |
+ a = a / b; |
+ a = a ~/ b; |
+ a = a % b; |
+ a = a + b; |
+ a = a + /*severe:StaticTypeError*/a; |
+ a = a - b; |
+ b = /*severe:StaticTypeError*/b - b; |
+ a = a << b; |
+ a = a >> b; |
+ a = a & b; |
+ a = a ^ b; |
+ a = a | b; |
+ c = (/*info:DynamicInvoke*/c + b); |
+ |
+ String x = 'hello'; |
+ int y = 42; |
+ x = x + x; |
+ x = x + /*info:DynamicCast*/c; |
+ x = x + /*severe:StaticTypeError*/y; |
+ |
+ bool p = true; |
+ p = p && p; |
+ p = p && /*info:DynamicCast*/c; |
+ p = (/*info:DynamicCast*/c) && p; |
+ p = (/*info:DynamicCast*/c) && /*info:DynamicCast*/c; |
+ p = (/*severe:StaticTypeError*/y) && p; |
+ p = c == y; |
+ |
+ a = a[b]; |
+ a = a[/*info:DynamicCast*/c]; |
+ c = (/*info:DynamicInvoke*/c[b]); |
+ a[/*severe:StaticTypeError*/y]; |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('compound assignments', { |
+ '/main.dart': ''' |
+ class A { |
+ A operator *(B b) {} |
+ A operator /(B b) {} |
+ A operator ~/(B b) {} |
+ A operator %(B b) {} |
+ A operator +(B b) {} |
+ A operator -(B b) {} |
+ A operator <<(B b) {} |
+ A operator >>(B b) {} |
+ A operator &(B b) {} |
+ A operator ^(B b) {} |
+ A operator |(B b) {} |
+ D operator [](B index) {} |
+ void operator []=(B index, D value) {} |
+ } |
+ |
+ class B { |
+ A operator -(B b) {} |
+ } |
+ |
+ class D { |
+ D operator +(D d) {} |
+ } |
+ |
+ foo() => new A(); |
+ |
+ test() { |
+ int x = 0; |
+ x += 5; |
+ (/*severe:StaticTypeError*/x += 3.14); |
+ |
+ double y = 0.0; |
+ y += 5; |
+ y += 3.14; |
+ |
+ num z = 0; |
+ z += 5; |
+ z += 3.14; |
+ |
+ x = /*info:DownCastImplicit*/x + z; |
+ x += /*info:DownCastImplicit*/z; |
+ y = /*info:DownCastImplicit*/y + z; |
+ y += /*info:DownCastImplicit*/z; |
+ |
+ dynamic w = 42; |
+ x += /*info:DynamicCast*/w; |
+ y += /*info:DynamicCast*/w; |
+ z += /*info:DynamicCast*/w; |
+ |
+ A a = new A(); |
+ B b = new B(); |
+ var c = foo(); |
+ a = a * b; |
+ a *= b; |
+ a *= /*info:DynamicCast*/c; |
+ a /= b; |
+ a ~/= b; |
+ a %= b; |
+ a += b; |
+ a += /*severe:StaticTypeError*/a; |
+ a -= b; |
+ (/*severe:StaticTypeError*/b -= b); |
+ a <<= b; |
+ a >>= b; |
+ a &= b; |
+ a ^= b; |
+ a |= b; |
+ (/*info:DynamicInvoke*/c += b); |
+ |
+ var d = new D(); |
+ a[b] += d; |
+ a[/*info:DynamicCast*/c] += d; |
+ a[/*severe:StaticTypeError*/z] += d; |
+ a[b] += /*info:DynamicCast*/c; |
+ a[b] += /*severe:StaticTypeError*/z; |
+ (/*info:DynamicInvoke*/(/*info:DynamicInvoke*/c[b]) += d); |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('super call placement', { |
+ '/main.dart': ''' |
+ class Base { |
+ var x; |
+ Base() : x = print('Base.1') { print('Base.2'); } |
+ } |
+ |
+ class Derived extends Base { |
+ var y, z; |
+ Derived() |
+ : y = print('Derived.1'), |
+ /*severe:InvalidSuperInvocation*/super(), |
+ z = print('Derived.2') { |
+ print('Derived.3'); |
+ } |
+ } |
+ |
+ class Valid extends Base { |
+ var y, z; |
+ Valid() |
+ : y = print('Valid.1'), |
+ z = print('Valid.2'), |
+ super() { |
+ print('Valid.3'); |
+ } |
+ } |
+ |
+ class AlsoValid extends Base { |
+ AlsoValid() : super(); |
+ } |
+ |
+ main() => new Derived(); |
+ ''' |
+ }); |
+ |
+ testChecker('for loop variable', { |
+ '/main.dart': ''' |
+ foo() { |
+ for (int i = 0; i < 10; i++) { |
+ i = /*severe:StaticTypeError*/"hi"; |
+ } |
+ } |
+ bar() { |
+ for (var i = 0; i < 10; i++) { |
+ int j = i + 1; |
+ } |
+ } |
+ ''' |
+ }); |
+ |
+ group('invalid overrides', () { |
+ testChecker('child override', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ class Base { |
+ A f; |
+ } |
+ |
+ class T1 extends Base { |
+ /*severe:InvalidMethodOverride*/B get f => null; |
+ } |
+ |
+ class T2 extends Base { |
+ /*severe:InvalidMethodOverride*/set f(B b) => null; |
+ } |
+ |
+ class T3 extends Base { |
+ /*severe:InvalidMethodOverride*/final B f; |
+ } |
+ class T4 extends Base { |
+ // two: one for the getter one for the setter. |
+ /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/B f; |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('child override 2', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ class Base { |
+ m(A a) {} |
+ } |
+ |
+ class Test extends Base { |
+ /*severe:InvalidMethodOverride*/m(B a) {} |
+ } |
+ ''' |
+ }); |
+ testChecker('grandchild override', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ class Grandparent { |
+ m(A a) {} |
+ } |
+ class Parent extends Grandparent { |
+ } |
+ |
+ class Test extends Parent { |
+ /*severe:InvalidMethodOverride*/m(B a) {} |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('double override', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ class Grandparent { |
+ m(A a) {} |
+ } |
+ class Parent extends Grandparent { |
+ m(A a) {} |
+ } |
+ |
+ class Test extends Parent { |
+ // Reported only once |
+ /*severe:InvalidMethodOverride*/m(B a) {} |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('double override 2', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ class Grandparent { |
+ m(A a) {} |
+ } |
+ class Parent extends Grandparent { |
+ /*severe:InvalidMethodOverride*/m(B a) {} |
+ } |
+ |
+ class Test extends Parent { |
+ m(B a) {} |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('mixin override to base', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ class Base { |
+ m(A a) {} |
+ } |
+ |
+ class M1 { |
+ m(B a) {} |
+ } |
+ |
+ class M2 {} |
+ |
+ class T1 extends Base with /*severe:InvalidMethodOverride*/M1 {} |
+ class T2 extends Base with /*severe:InvalidMethodOverride*/M1, M2 {} |
+ class T3 extends Base with M2, /*severe:InvalidMethodOverride*/M1 {} |
+ ''' |
+ }); |
+ |
+ testChecker('mixin override to mixin', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ class Base { |
+ } |
+ |
+ class M1 { |
+ m(B a) {} |
+ } |
+ |
+ class M2 { |
+ m(A a) {} |
+ } |
+ |
+ class T1 extends Base with M1, /*severe:InvalidMethodOverride*/M2 {} |
+ ''' |
+ }); |
+ |
+ // This is a regression test for a bug in an earlier implementation were |
+ // names were hiding errors if the first mixin override looked correct, |
+ // but subsequent ones did not. |
+ testChecker('no duplicate mixin override', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ class Base { |
+ m(A a) {} |
+ } |
+ |
+ class M1 { |
+ m(A a) {} |
+ } |
+ |
+ class M2 { |
+ m(B a) {} |
+ } |
+ |
+ class M3 { |
+ m(B a) {} |
+ } |
+ |
+ class T1 extends Base |
+ with M1, /*severe:InvalidMethodOverride*/M2, M3 {} |
+ ''' |
+ }); |
+ |
+ testChecker('class override of interface', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I { |
+ m(A a); |
+ } |
+ |
+ class T1 implements I { |
+ /*severe:InvalidMethodOverride*/m(B a) {} |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('base class override to child interface', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I { |
+ m(A a); |
+ } |
+ |
+ class Base { |
+ m(B a) {} |
+ } |
+ |
+ |
+ class T1 /*severe:InvalidMethodOverride*/extends Base implements I { |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('mixin override of interface', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I { |
+ m(A a); |
+ } |
+ |
+ class M { |
+ m(B a) {} |
+ } |
+ |
+ class T1 extends Object with /*severe:InvalidMethodOverride*/M |
+ implements I {} |
+ ''' |
+ }); |
+ |
+ // This is a case were it is incorrect to say that the base class |
+ // incorrectly overrides the interface. |
+ testChecker( |
+ 'no errors if subclass correctly overrides base and interface', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ class Base { |
+ m(A a) {} |
+ } |
+ |
+ class I1 { |
+ m(B a) {} |
+ } |
+ |
+ class T1 /*severe:InvalidMethodOverride*/extends Base |
+ implements I1 {} |
+ |
+ class T2 extends Base implements I1 { |
+ /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/m(a) {} |
+ } |
+ |
+ class T3 extends Object with /*severe:InvalidMethodOverride*/Base |
+ implements I1 {} |
+ |
+ class T4 extends Object with Base implements I1 { |
+ /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/m(a) {} |
+ } |
+ ''' |
+ }); |
+ }); |
+ |
+ group('class override of grand interface', () { |
+ testChecker('interface of interface of child', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ abstract class I2 implements I1 {} |
+ |
+ class T1 implements I2 { |
+ /*severe:InvalidMethodOverride*/m(B a) {} |
+ } |
+ ''' |
+ }); |
+ testChecker('superclass of interface of child', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ abstract class I2 extends I1 {} |
+ |
+ class T1 implements I2 { |
+ /*severe:InvalidMethodOverride*/m(B a) {} |
+ } |
+ ''' |
+ }); |
+ testChecker('mixin of interface of child', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class M1 { |
+ m(A a); |
+ } |
+ abstract class I2 extends Object with M1 {} |
+ |
+ class T1 implements I2 { |
+ /*severe:InvalidMethodOverride*/m(B a) {} |
+ } |
+ ''' |
+ }); |
+ testChecker('interface of abstract superclass', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ abstract class Base implements I1 {} |
+ |
+ class T1 extends Base { |
+ /*severe:InvalidMethodOverride*/m(B a) {} |
+ } |
+ ''' |
+ }); |
+ testChecker('interface of concrete superclass', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ |
+ // See issue #25 |
+ /*pass should be warning:AnalyzerError*/class Base implements I1 { |
+ } |
+ |
+ class T1 extends Base { |
+ // not reported technically because if the class is concrete, |
+ // it should implement all its interfaces and hence it is |
+ // sufficient to check overrides against it. |
+ m(B a) {} |
+ } |
+ ''' |
+ }); |
+ }); |
+ |
+ group('mixin override of grand interface', () { |
+ testChecker('interface of interface of child', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ abstract class I2 implements I1 {} |
+ |
+ class M { |
+ m(B a) {} |
+ } |
+ |
+ class T1 extends Object with /*severe:InvalidMethodOverride*/M |
+ implements I2 { |
+ } |
+ ''' |
+ }); |
+ testChecker('superclass of interface of child', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ abstract class I2 extends I1 {} |
+ |
+ class M { |
+ m(B a) {} |
+ } |
+ |
+ class T1 extends Object with /*severe:InvalidMethodOverride*/M |
+ implements I2 { |
+ } |
+ ''' |
+ }); |
+ testChecker('mixin of interface of child', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class M1 { |
+ m(A a); |
+ } |
+ abstract class I2 extends Object with M1 {} |
+ |
+ class M { |
+ m(B a) {} |
+ } |
+ |
+ class T1 extends Object with /*severe:InvalidMethodOverride*/M |
+ implements I2 { |
+ } |
+ ''' |
+ }); |
+ testChecker('interface of abstract superclass', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ abstract class Base implements I1 {} |
+ |
+ class M { |
+ m(B a) {} |
+ } |
+ |
+ class T1 extends Base with /*severe:InvalidMethodOverride*/M { |
+ } |
+ ''' |
+ }); |
+ testChecker('interface of concrete superclass', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ |
+ // See issue #25 |
+ /*pass should be warning:AnalyzerError*/class Base implements I1 { |
+ } |
+ |
+ class M { |
+ m(B a) {} |
+ } |
+ |
+ class T1 extends Base with M { |
+ } |
+ ''' |
+ }); |
+ }); |
+ |
+ group('superclass override of grand interface', () { |
+ testChecker('interface of interface of child', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ abstract class I2 implements I1 {} |
+ |
+ class Base { |
+ m(B a) {} |
+ } |
+ |
+ class T1 /*severe:InvalidMethodOverride*/extends Base |
+ implements I2 { |
+ } |
+ ''' |
+ }); |
+ testChecker('superclass of interface of child', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ abstract class I2 extends I1 {} |
+ |
+ class Base { |
+ m(B a) {} |
+ } |
+ |
+ class T1 /*severe:InvalidMethodOverride*/extends Base |
+ implements I2 { |
+ } |
+ ''' |
+ }); |
+ testChecker('mixin of interface of child', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class M1 { |
+ m(A a); |
+ } |
+ abstract class I2 extends Object with M1 {} |
+ |
+ class Base { |
+ m(B a) {} |
+ } |
+ |
+ class T1 /*severe:InvalidMethodOverride*/extends Base |
+ implements I2 { |
+ } |
+ ''' |
+ }); |
+ testChecker('interface of abstract superclass', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ |
+ abstract class Base implements I1 { |
+ /*severe:InvalidMethodOverride*/m(B a) {} |
+ } |
+ |
+ class T1 extends Base { |
+ // we consider the base class incomplete because it is |
+ // abstract, so we report the error here too. |
+ // TODO(sigmund): consider tracking overrides in a fine-grain |
+ // manner, then this and the double-overrides would not be |
+ // reported. |
+ /*severe:InvalidMethodOverride*/m(B a) {} |
+ } |
+ ''' |
+ }); |
+ testChecker('interface of concrete superclass', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ |
+ class Base implements I1 { |
+ /*severe:InvalidMethodOverride*/m(B a) {} |
+ } |
+ |
+ class T1 extends Base { |
+ m(B a) {} |
+ } |
+ ''' |
+ }); |
+ }); |
+ |
+ group('no duplicate reports from overriding interfaces', () { |
+ testChecker('type overrides same method in multiple interfaces', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ abstract class I2 implements I1 { |
+ m(A a); |
+ } |
+ |
+ class Base { |
+ } |
+ |
+ class T1 implements I2 { |
+ /*severe:InvalidMethodOverride*/m(B a) {} |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('type and base type override same method in interface', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ |
+ class Base { |
+ m(B a); |
+ } |
+ |
+ // Note: no error reported in `extends Base` to avoid duplicating |
+ // the error in T1. |
+ class T1 extends Base implements I1 { |
+ /*severe:InvalidMethodOverride*/m(B a) {} |
+ } |
+ |
+ // If there is no error in the class, we do report the error at |
+ // the base class: |
+ class T2 /*severe:InvalidMethodOverride*/extends Base |
+ implements I1 { |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('type and mixin override same method in interface', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ |
+ class M { |
+ m(B a); |
+ } |
+ |
+ class T1 extends Object with M implements I1 { |
+ /*severe:InvalidMethodOverride*/m(B a) {} |
+ } |
+ |
+ class T2 extends Object with /*severe:InvalidMethodOverride*/M |
+ implements I1 { |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('two grand types override same method in interface', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ |
+ class Grandparent { |
+ m(B a) {} |
+ } |
+ |
+ class Parent1 extends Grandparent { |
+ m(B a) {} |
+ } |
+ class Parent2 extends Grandparent { |
+ } |
+ |
+ // Note: otherwise both errors would be reported on this line |
+ class T1 /*severe:InvalidMethodOverride*/extends Parent1 |
+ implements I1 { |
+ } |
+ class T2 /*severe:InvalidMethodOverride*/extends Parent2 |
+ implements I1 { |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('two mixins override same method in interface', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ |
+ class M1 { |
+ m(B a) {} |
+ } |
+ |
+ class M2 { |
+ m(B a) {} |
+ } |
+ |
+ // Here we want to report both, because the error location is |
+ // different. |
+ // TODO(sigmund): should we merge these as well? |
+ class T1 extends Object |
+ with /*severe:InvalidMethodOverride*/M1 |
+ with /*severe:InvalidMethodOverride*/M2 |
+ implements I1 { |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('base type and mixin override same method in interface', { |
+ '/main.dart': ''' |
+ class A {} |
+ class B {} |
+ |
+ abstract class I1 { |
+ m(A a); |
+ } |
+ |
+ class Base { |
+ m(B a) {} |
+ } |
+ |
+ class M { |
+ m(B a) {} |
+ } |
+ |
+ // Here we want to report both, because the error location is |
+ // different. |
+ // TODO(sigmund): should we merge these as well? |
+ class T1 /*severe:InvalidMethodOverride*/extends Base |
+ with /*severe:InvalidMethodOverride*/M |
+ implements I1 { |
+ } |
+ ''' |
+ }); |
+ }); |
+ |
+ testChecker('invalid runtime checks', { |
+ '/main.dart': ''' |
+ typedef int I2I(int x); |
+ typedef int D2I(x); |
+ typedef int II2I(int x, int y); |
+ typedef int DI2I(x, int y); |
+ typedef int ID2I(int x, y); |
+ typedef int DD2I(x, y); |
+ |
+ typedef I2D(int x); |
+ typedef D2D(x); |
+ typedef II2D(int x, int y); |
+ typedef DI2D(x, int y); |
+ typedef ID2D(int x, y); |
+ typedef DD2D(x, y); |
+ |
+ int foo(int x) => x; |
+ int bar(int x, int y) => x + y; |
+ |
+ void main() { |
+ bool b; |
+ b = /*info:NonGroundTypeCheckInfo*/foo is I2I; |
+ b = /*info:NonGroundTypeCheckInfo*/foo is D2I; |
+ b = /*info:NonGroundTypeCheckInfo*/foo is I2D; |
+ b = foo is D2D; |
+ |
+ b = /*info:NonGroundTypeCheckInfo*/bar is II2I; |
+ b = /*info:NonGroundTypeCheckInfo*/bar is DI2I; |
+ b = /*info:NonGroundTypeCheckInfo*/bar is ID2I; |
+ b = /*info:NonGroundTypeCheckInfo*/bar is II2D; |
+ b = /*info:NonGroundTypeCheckInfo*/bar is DD2I; |
+ b = /*info:NonGroundTypeCheckInfo*/bar is DI2D; |
+ b = /*info:NonGroundTypeCheckInfo*/bar is ID2D; |
+ b = bar is DD2D; |
+ |
+ // For as, the validity of checks is deferred to runtime. |
+ Function f; |
+ f = foo as I2I; |
+ f = foo as D2I; |
+ f = foo as I2D; |
+ f = foo as D2D; |
+ |
+ f = bar as II2I; |
+ f = bar as DI2I; |
+ f = bar as ID2I; |
+ f = bar as II2D; |
+ f = bar as DD2I; |
+ f = bar as DI2D; |
+ f = bar as ID2D; |
+ f = bar as DD2D; |
+ } |
+ ''' |
+ }); |
+ |
+ group('function modifiers', () { |
+ testChecker('async', { |
+ '/main.dart': ''' |
+ import 'dart:async'; |
+ import 'dart:math' show Random; |
+ |
+ dynamic x; |
+ |
+ foo1() async => x; |
+ Future foo2() async => x; |
+ Future<int> foo3() async => (/*info:DynamicCast*/x); |
+ Future<int> foo4() async => (/*severe:StaticTypeError*/new Future<int>.value(/*info:DynamicCast*/x)); |
+ |
+ bar1() async { return x; } |
+ Future bar2() async { return x; } |
+ Future<int> bar3() async { return (/*info:DynamicCast*/x); } |
+ Future<int> bar4() async { return (/*severe:StaticTypeError*/new Future<int>.value(/*info:DynamicCast*/x)); } |
+ |
+ int y; |
+ Future<int> z; |
+ |
+ void baz() async { |
+ int a = /*info:DynamicCast*/await x; |
+ int b = await y; |
+ int c = await z; |
+ String d = /*severe:StaticTypeError*/await z; |
+ } |
+ |
+ Future<bool> get issue_264 async { |
+ await 42; |
+ if (new Random().nextBool()) { |
+ return true; |
+ } else { |
+ return /*severe:StaticTypeError*/new Future<bool>.value(false); |
+ } |
+ } |
+ ''' |
+ }); |
+ |
+ testChecker('async*', { |
+ '/main.dart': ''' |
+ import 'dart:async'; |
+ |
+ dynamic x; |
+ |
+ bar1() async* { yield x; } |
+ Stream bar2() async* { yield x; } |
+ Stream<int> bar3() async* { yield (/*info:DynamicCast*/x); } |
+ Stream<int> bar4() async* { yield (/*severe:StaticTypeError*/new Stream<int>()); } |
+ |
+ baz1() async* { yield* (/*info:DynamicCast*/x); } |
+ Stream baz2() async* { yield* (/*info:DynamicCast*/x); } |
+ Stream<int> baz3() async* { yield* (/*warning:DownCastComposite*/x); } |
+ Stream<int> baz4() async* { yield* new Stream<int>(); } |
+ Stream<int> baz5() async* { yield* (/*info:InferredTypeAllocation*/new Stream()); } |
+ ''' |
+ }); |
+ |
+ testChecker('sync*', { |
+ '/main.dart': ''' |
+ import 'dart:async'; |
+ |
+ dynamic x; |
+ |
+ bar1() sync* { yield x; } |
+ Iterable bar2() sync* { yield x; } |
+ Iterable<int> bar3() sync* { yield (/*info:DynamicCast*/x); } |
+ Iterable<int> bar4() sync* { yield (/*severe:StaticTypeError*/new Iterable<int>()); } |
+ |
+ baz1() sync* { yield* (/*info:DynamicCast*/x); } |
+ Iterable baz2() sync* { yield* (/*info:DynamicCast*/x); } |
+ Iterable<int> baz3() sync* { yield* (/*warning:DownCastComposite*/x); } |
+ Iterable<int> baz4() sync* { yield* new Iterable<int>(); } |
+ Iterable<int> baz5() sync* { yield* (/*info:InferredTypeAllocation*/new Iterable()); } |
+ ''' |
+ }); |
+ }); |
+} |