Index: pkg/analyzer/test/src/task/strong/checker_test.dart |
diff --git a/pkg/analyzer/test/src/task/strong/checker_test.dart b/pkg/analyzer/test/src/task/strong/checker_test.dart |
index 5fbc192248eaba9f57ec8806423490dce6dd990b..8a6376dac3265dc9ad457d3a986f8a4c8a7ae737 100644 |
--- a/pkg/analyzer/test/src/task/strong/checker_test.dart |
+++ b/pkg/analyzer/test/src/task/strong/checker_test.dart |
@@ -7,3310 +7,3296 @@ |
/// General type checking tests |
library analyzer.test.src.task.strong.checker_test; |
-import 'package:unittest/unittest.dart'; |
- |
+import '../../../reflective_tests.dart'; |
import 'strong_test_helper.dart'; |
void main() { |
initStrongModeTests(); |
+ runReflectiveTests(CheckerTest); |
+} |
- test('ternary operator', () { |
- checkFile(''' |
- 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; |
- |
- // The warning on assigning to _comparator is legitimate. Since K has |
- // no bound, all we know is that it's object. _comparator's function |
- // type is effectively: (Object, Object) -> int |
- // We are assigning it a fn of type: (Comparable, Comparable) -> int |
- // There's no telling if that will work. For example, consider: |
- // |
- // new SplayTreeMap<Uri>(); |
- // |
- // This would end up calling .compareTo() on a Uri, which doesn't |
- // define that since it doesn't implement Comparable. |
- SplayTreeMap([int compare(K key1, K key2), |
- bool isValidKey(potentialKey)]) |
- : _comparator = /*warning:DOWN_CAST_COMPOSITE*/(compare == null) ? Comparable.compare : compare, |
- _validKey = (isValidKey != null) ? isValidKey : ((v) => true) { |
- _Predicate<Object> v = (isValidKey != null) |
- ? isValidKey : (/*info:INFERRED_TYPE_CLOSURE*/(_) => true); |
- |
- v = (isValidKey != null) |
- ? v : (/*info:INFERRED_TYPE_CLOSURE*/(_) => true); |
- } |
- } |
- void main() { |
- Object obj = 42; |
- dynamic dyn = 42; |
- int i = 42; |
- |
- // Check the boolean conversion of the condition. |
- print(/*warning:NON_BOOL_CONDITION*/i ? false : true); |
- print((/*info:DOWN_CAST_IMPLICIT*/obj) ? false : true); |
- print((/*info:DYNAMIC_CAST*/dyn) ? false : true); |
- } |
- '''); |
- }); |
- |
- test('least upper bounds', () { |
- checkFile(''' |
- typedef T Returns<T>(); |
- |
- // regression test for https://github.com/dart-lang/sdk/issues/26094 |
- class A <S extends Returns<S>, T extends Returns<T>> { |
- int test(bool b) { |
- S s; |
- T t; |
- if (b) { |
- return /*warning:RETURN_OF_INVALID_TYPE*/b ? s : t; |
- } else { |
- return /*warning:RETURN_OF_INVALID_TYPE*/s ?? t; |
- } |
- } |
- } |
- |
- class B<S, T extends S> { |
- T t; |
- S s; |
- int test(bool b) { |
- return /*warning:RETURN_OF_INVALID_TYPE*/b ? t : s; |
- } |
- } |
- |
- class C { |
- // Check that the least upper bound of two types with the same |
- // class but different type arguments produces the pointwise |
- // least upper bound of the type arguments |
- int test1(bool b) { |
- List<int> li; |
- List<double> ld; |
- return /*warning:RETURN_OF_INVALID_TYPE*/b ? li : ld; |
- } |
- // TODO(leafp): This case isn't handled yet. This test checks |
- // the case where two related classes are instantiated with related |
- // but different types. |
- Iterable<num> test2(bool b) { |
- List<int> li; |
- Iterable<double> id; |
- int x = |
- /*info:ASSIGNMENT_CAST should be warning:INVALID_ASSIGNMENT*/ |
- b ? li : id; |
- return /*warning:DOWN_CAST_COMPOSITE should be pass*/b ? li : id; |
- } |
- } |
- '''); |
- }); |
- |
- test('setter return types', () { |
- checkFile(''' |
- void voidFn() => null; |
- class A { |
- set a(y) => 4; |
- set b(y) => voidFn(); |
- void set c(y) => /*warning:RETURN_OF_INVALID_TYPE*/4; |
- void set d(y) => voidFn(); |
- /*warning:NON_VOID_RETURN_FOR_SETTER*/int set e(y) => 4; |
- /*warning:NON_VOID_RETURN_FOR_SETTER*/int set f(y) => |
- /*warning:RETURN_OF_INVALID_TYPE*/voidFn(); |
- set g(y) {return /*warning:RETURN_OF_INVALID_TYPE*/4;} |
- void set h(y) {return /*warning:RETURN_OF_INVALID_TYPE*/4;} |
- /*warning:NON_VOID_RETURN_FOR_SETTER*/int set i(y) {return 4;} |
- } |
- '''); |
- }); |
- |
- test('if/for/do/while statements use boolean conversion', () { |
- checkFile(''' |
- main() { |
- dynamic dyn = 42; |
- Object obj = 42; |
- int i = 42; |
- bool b = false; |
- |
- if (b) {} |
- if (/*info:DYNAMIC_CAST*/dyn) {} |
- if (/*info:DOWN_CAST_IMPLICIT*/obj) {} |
- if (/*warning:NON_BOOL_CONDITION*/i) {} |
- |
- while (b) {} |
- while (/*info:DYNAMIC_CAST*/dyn) {} |
- while (/*info:DOWN_CAST_IMPLICIT*/obj) {} |
- while (/*warning:NON_BOOL_CONDITION*/i) {} |
- |
- do {} while (b); |
- do {} while (/*info:DYNAMIC_CAST*/dyn); |
- do {} while (/*info:DOWN_CAST_IMPLICIT*/obj); |
- do {} while (/*warning:NON_BOOL_CONDITION*/i); |
- |
- for (;b;) {} |
- for (;/*info:DYNAMIC_CAST*/dyn;) {} |
- for (;/*info:DOWN_CAST_IMPLICIT*/obj;) {} |
- for (;/*warning:NON_BOOL_CONDITION*/i;) {} |
- } |
- '''); |
- }); |
- |
- test('for-in casts supertype sequence to iterable', () { |
- checkFile(''' |
- main() { |
- dynamic d; |
- for (var i in /*info:DYNAMIC_CAST*/d) {} |
- |
- Object o; |
- for (var i in /*info:DOWN_CAST_IMPLICIT*/o) {} |
- } |
- '''); |
- }); |
- |
- test('await for-in casts supertype sequence to stream', () { |
- checkFile(''' |
- main() async { |
- dynamic d; |
- await for (var i in /*info:DYNAMIC_CAST*/d) {} |
- |
- Object o; |
- await for (var i in /*info:DOWN_CAST_IMPLICIT*/o) {} |
- } |
- '''); |
- }); |
- |
- test('for-in casts iterable element to variable', () { |
- checkFile(''' |
- main() { |
- // Don't choke if sequence is not iterable. |
- for (var i in /*warning:FOR_IN_OF_INVALID_TYPE*/1234) {} |
- |
- // Dynamic cast. |
- for (String /*info:DYNAMIC_CAST*/s in <dynamic>[]) {} |
- |
- // Identity cast. |
- for (String s in <String>[]) {} |
- |
- // Untyped. |
- for (var s in <String>[]) {} |
- |
- // Downcast. |
- for (int /*info:DOWN_CAST_IMPLICIT*/i in <num>[]) {} |
- } |
- '''); |
- }); |
- |
- test('await for-in casts stream element to variable', () { |
- checkFile(''' |
- import 'dart:async'; |
- main() async { |
- // Don't choke if sequence is not stream. |
- await for (var i in /*warning:FOR_IN_OF_INVALID_TYPE*/1234) {} |
- |
- // Dynamic cast. |
- await for (String /*info:DYNAMIC_CAST*/s in new Stream<dynamic>()) {} |
- |
- // Identity cast. |
- await for (String s in new Stream<String>()) {} |
- |
- // Untyped. |
- await for (var s in new Stream<String>()) {} |
- |
- // Downcast. |
- await for (int /*info:DOWN_CAST_IMPLICIT*/i in new Stream<num>()) {} |
- } |
- '''); |
- }); |
- |
- test('dynamic invocation', () { |
- checkFile(''' |
- typedef dynamic A(dynamic x); |
- class B { |
- int call(int x) => x; |
- double col(double x) => x; |
- } |
- void main() { |
- { |
- B f = new B(); |
- int x; |
- double y; |
- x = f(3); |
- x = /*warning:INVALID_ASSIGNMENT*/f.col(3.0); |
- y = /*warning:INVALID_ASSIGNMENT*/f(3); |
- y = f.col(3.0); |
- f(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3.0); |
- f.col(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3); |
- } |
- { |
- Function f = new B(); |
- int x; |
- double y; |
- x = /*info:DYNAMIC_CAST, info:DYNAMIC_INVOKE*/f(3); |
- x = /*info:DYNAMIC_CAST, info:DYNAMIC_INVOKE, info:INVALID_ASSIGNMENT*/f.col(3.0); |
- y = /*info:DYNAMIC_CAST, info:DYNAMIC_INVOKE*/f(3); |
- y = /*info:DYNAMIC_CAST, info:DYNAMIC_INVOKE*/f.col(3.0); |
- /*info:DYNAMIC_INVOKE*/f(3.0); |
- // Through type propagation, we know f is actually a B, hence the |
- // hint. |
- /*info:DYNAMIC_INVOKE*/f.col(/*info:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3); |
- } |
- { |
- A f = new B(); |
- int x; |
- double y; |
- x = /*info:DYNAMIC_CAST, info:DYNAMIC_INVOKE*/f(3); |
- y = /*info:DYNAMIC_CAST, info:DYNAMIC_INVOKE*/f(3); |
- /*info:DYNAMIC_INVOKE*/f(3.0); |
- } |
- { |
- dynamic g = new B(); |
- /*info:DYNAMIC_INVOKE*/g.call(/*info:ARGUMENT_TYPE_NOT_ASSIGNABLE*/32.0); |
- /*info:DYNAMIC_INVOKE*/g.col(42.0); |
- /*info:DYNAMIC_INVOKE*/g.foo(42.0); |
- /*info:DYNAMIC_INVOKE*/g./*info:UNDEFINED_GETTER*/x; |
- A f = new B(); |
- /*info:DYNAMIC_INVOKE*/f.col(42.0); |
- /*info:DYNAMIC_INVOKE*/f.foo(42.0); |
- /*info:DYNAMIC_INVOKE*/f./*warning:UNDEFINED_GETTER*/x; |
- } |
- } |
- '''); |
- }); |
- |
- test('conversion and dynamic invoke', () { |
- addFile( |
- ''' |
- dynamic toString = (int x) => x + 42; |
- dynamic hashCode = "hello"; |
- ''', |
- name: '/helper.dart'); |
+@reflectiveTest |
+class CheckerTest { |
+ void test_awaitForInCastsStreamElementToVariable() { |
checkFile(''' |
- import 'helper.dart' as helper; |
+import 'dart:async'; |
+main() async { |
+ // Don't choke if sequence is not stream. |
+ await for (var i in /*warning:FOR_IN_OF_INVALID_TYPE*/1234) {} |
- class A { |
- String x = "hello world"; |
- |
- void baz1(y) { x + /*info:DYNAMIC_CAST*/y; } |
- static baz2(y) => /*info:DYNAMIC_INVOKE*/y + y; |
- } |
- |
- void foo(String str) { |
- print(str); |
- } |
- |
- class B { |
- String toString([int arg]) => arg.toString(); |
- } |
- |
- void bar(a) { |
- foo(/*info:DYNAMIC_CAST,info:DYNAMIC_INVOKE*/a.x); |
- } |
- |
- baz() => new B(); |
- |
- typedef DynFun(x); |
- typedef StrFun(String x); |
- |
- var bar1 = bar; |
- |
- void main() { |
- var a = new A(); |
- bar(a); |
- (/*info:DYNAMIC_INVOKE*/bar1(a)); |
- var b = bar; |
- (/*info:DYNAMIC_INVOKE*/b(a)); |
- var f1 = foo; |
- f1("hello"); |
- dynamic f2 = foo; |
- (/*info:DYNAMIC_INVOKE*/f2("hello")); |
- DynFun f3 = foo; |
- (/*info:DYNAMIC_INVOKE*/f3("hello")); |
- (/*info:DYNAMIC_INVOKE*/f3(42)); |
- StrFun f4 = foo; |
- f4("hello"); |
- a.baz1("hello"); |
- var b1 = a.baz1; |
- (/*info:DYNAMIC_INVOKE*/b1("hello")); |
- A.baz2("hello"); |
- var b2 = A.baz2; |
- (/*info:DYNAMIC_INVOKE*/b2("hello")); |
- |
- dynamic a1 = new B(); |
- (/*info:DYNAMIC_INVOKE*/a1./*info:UNDEFINED_GETTER*/x); |
- a1.toString(); |
- (/*info:DYNAMIC_INVOKE*/a1.toString(42)); |
- var toStringClosure = a1.toString; |
- (/*info:DYNAMIC_INVOKE*/a1.toStringClosure()); |
- (/*info:DYNAMIC_INVOKE*/a1.toStringClosure(42)); |
- (/*info:DYNAMIC_INVOKE*/a1.toStringClosure("hello")); |
- a1.hashCode; |
- |
- dynamic toString = () => null; |
- (/*info:DYNAMIC_INVOKE*/toString()); |
- |
- (/*info:DYNAMIC_INVOKE*/helper.toString()); |
- var toStringClosure2 = helper.toString; |
- (/*info:DYNAMIC_INVOKE*/toStringClosure2()); |
- int hashCode = /*info:DYNAMIC_CAST*/helper.hashCode; |
- |
- baz().toString(); |
- baz().hashCode; |
- } |
- '''); |
- }); |
- |
- test('Constructors', () { |
- checkFile(''' |
- const num z = 25; |
- Object obj = "world"; |
- |
- class A { |
- int x; |
- String y; |
- |
- A(this.x) : this.y = /*warning:FIELD_INITIALIZER_NOT_ASSIGNABLE*/42; |
- |
- A.c1(p): this.x = /*info:DOWN_CAST_IMPLICIT*/z, this.y = /*info:DYNAMIC_CAST*/p; |
- |
- A.c2(this.x, this.y); |
- |
- A.c3(/*severe:INVALID_PARAMETER_DECLARATION*/num this.x, String this.y); |
- } |
- |
- class B extends A { |
- B() : super(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello"); |
- |
- B.c2(int x, String y) : super.c2(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/y, |
- /*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/x); |
- |
- B.c3(num x, Object y) : super.c3(x, /*info:DOWN_CAST_IMPLICIT*/y); |
- } |
- |
- void main() { |
- A a = new A.c2(/*info:DOWN_CAST_IMPLICIT*/z, /*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/z); |
- var b = new B.c2(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello", /*info:DOWN_CAST_IMPLICIT*/obj); |
- } |
- '''); |
- }); |
- |
- test('Unbound variable', () { |
- checkFile(''' |
- void main() { |
- dynamic y = /*warning:UNDEFINED_IDENTIFIER should be error*/unboundVariable; |
- } |
- '''); |
- }); |
- |
- test('Unbound type name', () { |
- checkFile(''' |
- void main() { |
- /*warning:UNDEFINED_CLASS should be error*/AToB y; |
- } |
- '''); |
- }); |
- |
- // Regression test for https://github.com/dart-lang/sdk/issues/25069 |
- test('Void subtyping', () { |
- checkFile(''' |
- typedef int Foo(); |
- void foo() {} |
- void main () { |
- Foo x = /*warning:INVALID_ASSIGNMENT,info:USE_OF_VOID_RESULT*/foo(); |
- } |
- '''); |
- }); |
- |
- group('Ground type subtyping:', () { |
- test('dynamic is top', () { |
- checkFile(''' |
- |
- 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; |
- } |
- '''); |
- }); |
- |
- test('dynamic downcasts', () { |
- checkFile(''' |
- |
- 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:DYNAMIC_CAST*/y; |
- d = /*info:DYNAMIC_CAST*/y; |
- n = /*info:DYNAMIC_CAST*/y; |
- a = /*info:DYNAMIC_CAST*/y; |
- b = /*info:DYNAMIC_CAST*/y; |
- } |
- '''); |
- }); |
- |
- test('assigning a class', () { |
- checkFile(''' |
- |
- 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 = /*warning:INVALID_ASSIGNMENT*/a; |
- d = /*warning:INVALID_ASSIGNMENT*/a; |
- n = /*warning:INVALID_ASSIGNMENT*/a; |
- a = a; |
- b = /*info:DOWN_CAST_IMPLICIT*/a; |
- } |
- '''); |
- }); |
- |
- test('assigning a subclass', () { |
- checkFile(''' |
- |
- 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 = /*warning:INVALID_ASSIGNMENT*/b; |
- d = /*warning:INVALID_ASSIGNMENT*/b; |
- n = /*warning:INVALID_ASSIGNMENT*/b; |
- a = b; |
- b = b; |
- c = /*warning:INVALID_ASSIGNMENT*/b; |
- } |
- '''); |
- }); |
- |
- test('interfaces', () { |
- checkFile(''' |
- |
- 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:DOWN_CAST_IMPLICIT*/top; |
- left = left; |
- left = /*warning:INVALID_ASSIGNMENT*/right; |
- left = bot; |
- } |
- { |
- right = /*info:DOWN_CAST_IMPLICIT*/top; |
- right = /*warning:INVALID_ASSIGNMENT*/left; |
- right = right; |
- right = bot; |
- } |
- { |
- bot = /*info:DOWN_CAST_IMPLICIT*/top; |
- bot = /*info:DOWN_CAST_IMPLICIT*/left; |
- bot = /*info:DOWN_CAST_IMPLICIT*/right; |
- bot = bot; |
- } |
- } |
- '''); |
- }); |
- }); |
- |
- group('Function typing and subtyping:', () { |
- test('int and object', () { |
- checkFile(''' |
- |
- 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 globalTop(int x) => x; |
- int globalLeft(int x) => x; |
- Object globalRight(Object x) => x; |
- int bot_(Object x) => /*info:DOWN_CAST_IMPLICIT*/x; |
- int globalBot(Object x) => x as int; |
- |
- void main() { |
- // Note: use locals so we only know the type, not that it's a specific |
- // function declaration. (we can issue better errors in that case.) |
- var top = globalTop; |
- var left = globalLeft; |
- var right = globalRight; |
- var bot = globalBot; |
- |
- { // Check typedef equality |
- Left f = left; |
- Left2 g = f; |
- } |
- { |
- Top f; |
- f = top; |
- f = left; |
- f = right; |
- f = bot; |
- } |
- { |
- Left f; |
- f = /*warning:DOWN_CAST_COMPOSITE*/top; |
- f = left; |
- f = /*warning:DOWN_CAST_COMPOSITE*/right; // Should we reject this? |
- f = bot; |
- } |
- { |
- Right f; |
- f = /*warning:DOWN_CAST_COMPOSITE*/top; |
- f = /*warning:DOWN_CAST_COMPOSITE*/left; // Should we reject this? |
- f = right; |
- f = bot; |
- } |
- { |
- Bot f; |
- f = /*warning:DOWN_CAST_COMPOSITE*/top; |
- f = /*warning:DOWN_CAST_COMPOSITE*/left; |
- f = /*warning:DOWN_CAST_COMPOSITE*/right; |
- f = bot; |
- } |
- } |
- '''); |
- }); |
- |
- test('classes', () { |
- checkFile(''' |
- |
- 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:DOWN_CAST_IMPLICIT*/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 = /*severe:STATIC_TYPE_ERROR*/top; |
- f = left; |
- f = /*severe:STATIC_TYPE_ERROR*/right; |
- f = bot; |
- } |
- { |
- Right f; |
- f = /*severe:STATIC_TYPE_ERROR*/top; |
- f = /*severe:STATIC_TYPE_ERROR*/left; |
- f = right; |
- f = bot; |
- } |
- { |
- Bot f; |
- f = /*severe:STATIC_TYPE_ERROR*/top; |
- f = /*severe:STATIC_TYPE_ERROR*/left; |
- f = /*severe:STATIC_TYPE_ERROR*/right; |
- f = bot; |
- } |
- } |
- '''); |
- }); |
- |
- test('dynamic', () { |
- checkFile(''' |
- |
- 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 |
- |
- void main() { |
- Top top; |
- Left left; |
- Right right; |
- Bottom bot; |
- { |
- Top f; |
- f = top; |
- f = left; |
- f = right; |
- f = bot; |
- } |
- { |
- Left f; |
- f = /*warning:DOWN_CAST_COMPOSITE*/top; |
- f = left; |
- f = /*warning:DOWN_CAST_COMPOSITE*/right; |
- f = bot; |
- } |
- { |
- Right f; |
- f = /*warning:DOWN_CAST_COMPOSITE*/top; |
- f = /*warning:DOWN_CAST_COMPOSITE*/left; |
- f = right; |
- f = bot; |
- } |
- { |
- Bottom f; |
- f = /*warning:DOWN_CAST_COMPOSITE*/top; |
- f = /*warning:DOWN_CAST_COMPOSITE*/left; |
- f = /*warning:DOWN_CAST_COMPOSITE*/right; |
- f = bot; |
- } |
- } |
- '''); |
- }); |
- |
- test('dynamic functions - closures are not fuzzy', () { |
- // Regression test for |
- // https://github.com/dart-lang/sdk/issues/26118 |
- // https://github.com/dart-lang/sdk/issues/26156 |
- checkFile(''' |
- void takesF(void f(int x)) {} |
- |
- typedef void TakesInt(int x); |
- |
- void update(_) {} |
- void updateOpt([_]) {} |
- void updateOptNum([num x]) {} |
- |
- class A { |
- TakesInt f; |
- A(TakesInt g) { |
- f = update; |
- f = updateOpt; |
- f = updateOptNum; |
- } |
- TakesInt g(bool a, bool b) { |
- if (a) { |
- return update; |
- } else if (b) { |
- return updateOpt; |
- } else { |
- return updateOptNum; |
- } |
- } |
- } |
- |
- void test0() { |
- takesF(update); |
- takesF(updateOpt); |
- takesF(updateOptNum); |
- TakesInt f; |
- f = update; |
- f = updateOpt; |
- f = updateOptNum; |
- new A(update); |
- new A(updateOpt); |
- new A(updateOptNum); |
- } |
- |
- void test1() { |
- void takesF(f(int x)) => null; |
- takesF((dynamic y) => 3); |
- } |
- |
- void test2() { |
- int x; |
- int f/*<T>*/(/*=T*/ t, callback(/*=T*/ x)) { return 3; } |
- f(x, (y) => 3); |
- } |
- '''); |
- }); |
- |
- test('dynamic - known functions', () { |
- // Our lattice should look like this: |
- // |
- // |
- // Bot -> Top |
- // / \ |
- // A -> Top Bot -> A |
- // / \ / |
- // Top -> Top A -> A |
- // \ / |
- // Top -> A |
- // |
- // Note that downcasts of known functions are promoted to |
- // static type errors, since they cannot succeed. |
- // This makes some of what look like downcasts turn into |
- // type errors below. |
- checkFile(''' |
- class A {} |
- |
- typedef dynamic BotTop(dynamic x); |
- typedef dynamic ATop(A x); |
- typedef A BotA(dynamic x); |
- typedef A AA(A x); |
- typedef A TopA(Object x); |
- typedef dynamic TopTop(Object x); |
- |
- dynamic aTop(A x) => x; |
- A aa(A x) => x; |
- dynamic topTop(dynamic x) => x; |
- A topA(dynamic x) => /*info:DYNAMIC_CAST*/x; |
- void apply/*<T>*/(/*=T*/ f0, /*=T*/ f1, /*=T*/ f2, |
- /*=T*/ f3, /*=T*/ f4, /*=T*/ f5) {} |
- void main() { |
- BotTop botTop; |
- BotA botA; |
- { |
- BotTop f; |
- f = topA; |
- f = topTop; |
- f = aa; |
- f = aTop; |
- f = botA; |
- f = botTop; |
- apply/*<BotTop>*/( |
- topA, |
- topTop, |
- aa, |
- aTop, |
- botA, |
- botTop |
- ); |
- apply/*<BotTop>*/( |
- (dynamic x) => new A(), |
- (dynamic x) => (x as Object), |
- (A x) => x, |
- (A x) => null, |
- botA, |
- botTop |
- ); |
- } |
- { |
- ATop f; |
- f = topA; |
- f = topTop; |
- f = aa; |
- f = aTop; |
- f = /*warning:DOWN_CAST_COMPOSITE should be severe:STATIC_TYPE_ERROR*/botA; |
- f = /*warning:DOWN_CAST_COMPOSITE*/botTop; |
- apply/*<ATop>*/( |
- topA, |
- topTop, |
- aa, |
- aTop, |
- /*warning:DOWN_CAST_COMPOSITE should be severe:STATIC_TYPE_ERROR*/botA, |
- /*warning:DOWN_CAST_COMPOSITE*/botTop |
- ); |
- apply/*<ATop>*/( |
- (dynamic x) => new A(), |
- (dynamic x) => (x as Object), |
- (A x) => x, |
- (A x) => null, |
- /*warning:DOWN_CAST_COMPOSITE should be severe:STATIC_TYPE_ERROR*/botA, |
- /*warning:DOWN_CAST_COMPOSITE*/botTop |
- ); |
- } |
- { |
- BotA f; |
- f = topA; |
- f = /*severe:STATIC_TYPE_ERROR*/topTop; |
- f = aa; |
- f = /*severe:STATIC_TYPE_ERROR*/aTop; |
- f = botA; |
- f = /*warning:DOWN_CAST_COMPOSITE*/botTop; |
- apply/*<BotA>*/( |
- topA, |
- /*severe:STATIC_TYPE_ERROR*/topTop, |
- aa, |
- /*severe:STATIC_TYPE_ERROR*/aTop, |
- botA, |
- /*warning:DOWN_CAST_COMPOSITE*/botTop |
- ); |
- apply/*<BotA>*/( |
- (dynamic x) => new A(), |
- /*severe:STATIC_TYPE_ERROR*/(dynamic x) => (x as Object), |
- (A x) => x, |
- /*severe:STATIC_TYPE_ERROR*/(A x) => (x as Object), |
- botA, |
- /*warning:DOWN_CAST_COMPOSITE*/botTop |
- ); |
- } |
- { |
- AA f; |
- f = topA; |
- f = /*severe:STATIC_TYPE_ERROR*/topTop; |
- f = aa; |
- f = /*severe:STATIC_TYPE_ERROR*/aTop; // known function |
- f = /*warning:DOWN_CAST_COMPOSITE*/botA; |
- f = /*warning:DOWN_CAST_COMPOSITE*/botTop; |
- apply/*<AA>*/( |
- topA, |
- /*severe:STATIC_TYPE_ERROR*/topTop, |
- aa, |
- /*severe:STATIC_TYPE_ERROR*/aTop, // known function |
- /*warning:DOWN_CAST_COMPOSITE*/botA, |
- /*warning:DOWN_CAST_COMPOSITE*/botTop |
- ); |
- apply/*<AA>*/( |
- (dynamic x) => new A(), |
- /*severe:STATIC_TYPE_ERROR*/(dynamic x) => (x as Object), |
- (A x) => x, |
- /*severe:STATIC_TYPE_ERROR*/(A x) => (x as Object), // known function |
- /*warning:DOWN_CAST_COMPOSITE*/botA, |
- /*warning:DOWN_CAST_COMPOSITE*/botTop |
- ); |
- } |
- { |
- TopTop f; |
- f = topA; |
- f = topTop; |
- f = /*severe:STATIC_TYPE_ERROR*/aa; |
- f = /*severe:STATIC_TYPE_ERROR*/aTop; // known function |
- f = /*warning:DOWN_CAST_COMPOSITE should be severe:STATIC_TYPE_ERROR*/botA; |
- f = /*warning:DOWN_CAST_COMPOSITE*/botTop; |
- apply/*<TopTop>*/( |
- topA, |
- topTop, |
- /*severe:STATIC_TYPE_ERROR*/aa, |
- /*severe:STATIC_TYPE_ERROR*/aTop, // known function |
- /*warning:DOWN_CAST_COMPOSITE should be severe:STATIC_TYPE_ERROR*/botA, |
- /*warning:DOWN_CAST_COMPOSITE*/botTop |
- ); |
- apply/*<TopTop>*/( |
- (dynamic x) => new A(), |
- (dynamic x) => (x as Object), |
- /*severe:STATIC_TYPE_ERROR*/(A x) => x, |
- /*severe:STATIC_TYPE_ERROR*/(A x) => (x as Object), // known function |
- /*warning:DOWN_CAST_COMPOSITE should be severe:STATIC_TYPE_ERROR*/botA, |
- /*warning:DOWN_CAST_COMPOSITE*/botTop |
- ); |
- } |
- { |
- TopA f; |
- f = topA; |
- f = /*severe:STATIC_TYPE_ERROR*/topTop; // known function |
- f = /*severe:STATIC_TYPE_ERROR*/aa; // known function |
- f = /*severe:STATIC_TYPE_ERROR*/aTop; // known function |
- f = /*warning:DOWN_CAST_COMPOSITE*/botA; |
- f = /*warning:DOWN_CAST_COMPOSITE*/botTop; |
- apply/*<TopA>*/( |
- topA, |
- /*severe:STATIC_TYPE_ERROR*/topTop, // known function |
- /*severe:STATIC_TYPE_ERROR*/aa, // known function |
- /*severe:STATIC_TYPE_ERROR*/aTop, // known function |
- /*warning:DOWN_CAST_COMPOSITE*/botA, |
- /*warning:DOWN_CAST_COMPOSITE*/botTop |
- ); |
- apply/*<TopA>*/( |
- (dynamic x) => new A(), |
- /*severe:STATIC_TYPE_ERROR*/(dynamic x) => (x as Object), // known function |
- /*severe:STATIC_TYPE_ERROR*/(A x) => x, // known function |
- /*severe:STATIC_TYPE_ERROR*/(A x) => (x as Object), // known function |
- /*warning:DOWN_CAST_COMPOSITE*/botA, |
- /*warning:DOWN_CAST_COMPOSITE*/botTop |
- ); |
- } |
- } |
- '''); |
- }); |
- |
- test('function literal variance', () { |
- checkFile(''' |
- |
- 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 = /*severe:STATIC_TYPE_ERROR*/top; |
- f = left; |
- f = /*severe:STATIC_TYPE_ERROR*/right; |
- f = bot; |
- } |
- { |
- Function2<A, A> f; |
- f = /*severe:STATIC_TYPE_ERROR*/top; |
- f = /*severe:STATIC_TYPE_ERROR*/left; |
- f = right; |
- f = bot; |
- } |
- { |
- Function2<A, B> f; |
- f = /*severe:STATIC_TYPE_ERROR*/top; |
- f = /*severe:STATIC_TYPE_ERROR*/left; |
- f = /*severe:STATIC_TYPE_ERROR*/right; |
- f = bot; |
- } |
- } |
- '''); |
- }); |
- |
- test('function variable variance', () { |
- checkFile(''' |
- |
- 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:DOWN_CAST_COMPOSITE*/top; |
- left = left; |
- left = /*warning:DOWN_CAST_COMPOSITE*/right; // Should we reject this? |
- left = bot; |
- |
- right = /*warning:DOWN_CAST_COMPOSITE*/top; |
- right = /*warning:DOWN_CAST_COMPOSITE*/left; // Should we reject this? |
- right = right; |
- right = bot; |
- |
- bot = /*warning:DOWN_CAST_COMPOSITE*/top; |
- bot = /*warning:DOWN_CAST_COMPOSITE*/left; |
- bot = /*warning:DOWN_CAST_COMPOSITE*/right; |
- bot = bot; |
- } |
- } |
- '''); |
- }); |
- |
- test('static method variance', () { |
- checkFile(''' |
- |
- class A {} |
- class B extends A {} |
- |
- class C { |
- static A top(B x) => x; |
- static B left(B x) => x; |
- static A right(A x) => x; |
- static B bot(A x) => x as B; |
- } |
- |
- typedef T Function2<S, T>(S z); |
- |
- void main() { |
- { |
- Function2<B, A> f; |
- f = C.top; |
- f = C.left; |
- f = C.right; |
- f = C.bot; |
- } |
- { |
- Function2<B, B> f; |
- f = /*severe:STATIC_TYPE_ERROR*/C.top; |
- f = C.left; |
- f = /*severe:STATIC_TYPE_ERROR*/C.right; |
- f = C.bot; |
- } |
- { |
- Function2<A, A> f; |
- f = /*severe:STATIC_TYPE_ERROR*/C.top; |
- f = /*severe:STATIC_TYPE_ERROR*/C.left; |
- f = C.right; |
- f = C.bot; |
- } |
- { |
- Function2<A, B> f; |
- f = /*severe:STATIC_TYPE_ERROR*/C.top; |
- f = /*severe:STATIC_TYPE_ERROR*/C.left; |
- f = /*severe:STATIC_TYPE_ERROR*/C.right; |
- f = C.bot; |
- } |
- } |
- '''); |
- }); |
- |
- test('instance method variance', () { |
- checkFile(''' |
- |
- class A {} |
- class B extends A {} |
- |
- class C { |
- A top(B x) => x; |
- B left(B x) => x; |
- A right(A x) => x; |
- B bot(A x) => x as B; |
- } |
- |
- typedef T Function2<S, T>(S z); |
- |
- void main() { |
- C c = new C(); |
- { |
- Function2<B, A> f; |
- f = c.top; |
- f = c.left; |
- f = c.right; |
- f = c.bot; |
- } |
- { |
- Function2<B, B> f; |
- f = /*warning:DOWN_CAST_COMPOSITE*/c.top; |
- f = c.left; |
- f = /*warning:DOWN_CAST_COMPOSITE*/c.right; |
- f = c.bot; |
- } |
- { |
- Function2<A, A> f; |
- f = /*warning:DOWN_CAST_COMPOSITE*/c.top; |
- f = /*warning:DOWN_CAST_COMPOSITE*/c.left; |
- f = c.right; |
- f = c.bot; |
- } |
- { |
- Function2<A, B> f; |
- f = /*warning:DOWN_CAST_COMPOSITE*/c.top; |
- f = /*warning:DOWN_CAST_COMPOSITE*/c.left; |
- f = /*warning:DOWN_CAST_COMPOSITE*/c.right; |
- f = c.bot; |
- } |
- } |
- '''); |
- }); |
- |
- test('higher order function literals 1', () { |
- checkFile(''' |
- |
- 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:DOWN_CAST_COMPOSITE*/f; |
- AToB bot(BToA 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 = /*severe:STATIC_TYPE_ERROR*/top; |
- f = left; |
- f = /*severe:STATIC_TYPE_ERROR*/right; |
- f = bot; |
- } |
- { |
- Function2<BToA, BToA> f; // Right |
- f = /*severe:STATIC_TYPE_ERROR*/top; |
- f = /*severe:STATIC_TYPE_ERROR*/left; |
- f = right; |
- f = bot; |
- } |
- { |
- Function2<BToA, AToB> f; // Bot |
- f = bot; |
- f = /*severe:STATIC_TYPE_ERROR*/left; |
- f = /*severe:STATIC_TYPE_ERROR*/top; |
- f = /*severe:STATIC_TYPE_ERROR*/left; |
- } |
- } |
- '''); |
- }); |
- |
- test('higher order function literals 2', () { |
- checkFile(''' |
- |
- 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 |
- |
- 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:DOWN_CAST_COMPOSITE*/f; |
- Function2<A, B> bot(BToA f) => f as Function2<A, B>; |
- |
- void main() { |
- { |
- Function2<AToB, BToA> f; // Top |
- f = top; |
- f = left; |
- f = right; |
- f = bot; |
- } |
- { |
- Function2<AToB, AToB> f; // Left |
- f = /*severe:STATIC_TYPE_ERROR*/top; |
- f = left; |
- f = /*severe:STATIC_TYPE_ERROR*/right; |
- f = bot; |
- } |
- { |
- Function2<BToA, BToA> f; // Right |
- f = /*severe:STATIC_TYPE_ERROR*/top; |
- f = /*severe:STATIC_TYPE_ERROR*/left; |
- f = right; |
- f = bot; |
- } |
- { |
- Function2<BToA, AToB> f; // Bot |
- f = bot; |
- f = /*severe:STATIC_TYPE_ERROR*/left; |
- f = /*severe:STATIC_TYPE_ERROR*/top; |
- f = /*severe:STATIC_TYPE_ERROR*/left; |
- } |
- } |
- '''); |
- }); |
- |
- test('higher order function literals 3', () { |
- checkFile(''' |
- |
- 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(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:DOWN_CAST_COMPOSITE*/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 = /*severe:STATIC_TYPE_ERROR*/top; |
- f = left; |
- f = /*severe:STATIC_TYPE_ERROR*/right; |
- f = bot; |
- } |
- { |
- Function2<BToA, BToA> f; // Right |
- f = /*severe:STATIC_TYPE_ERROR*/top; |
- f = /*severe:STATIC_TYPE_ERROR*/left; |
- f = right; |
- f = bot; |
- } |
- { |
- Function2<BToA, AToB> f; // Bot |
- f = bot; |
- f = /*severe:STATIC_TYPE_ERROR*/left; |
- f = /*severe:STATIC_TYPE_ERROR*/top; |
- f = /*severe:STATIC_TYPE_ERROR*/left; |
- } |
- } |
- '''); |
- }); |
- |
- test('higher order function variables', () { |
- checkFile(''' |
- |
- 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:DOWN_CAST_COMPOSITE*/top; |
- left = left; |
- left = |
- /*warning:DOWN_CAST_COMPOSITE should be severe:STATIC_TYPE_ERROR*/right; |
- left = bot; |
- |
- right = /*warning:DOWN_CAST_COMPOSITE*/top; |
- right = |
- /*warning:DOWN_CAST_COMPOSITE should be severe:STATIC_TYPE_ERROR*/left; |
- right = right; |
- right = bot; |
- |
- bot = /*warning:DOWN_CAST_COMPOSITE*/top; |
- bot = /*warning:DOWN_CAST_COMPOSITE*/left; |
- bot = /*warning:DOWN_CAST_COMPOSITE*/right; |
- bot = bot; |
- } |
- } |
- '''); |
- }); |
- |
- test('named and optional parameters', () { |
- checkFile(''' |
- |
- 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 = /*warning:INVALID_ASSIGNMENT*/n; |
- r = /*warning:INVALID_ASSIGNMENT*/rr; |
- r = ro; |
- r = rn; |
- r = oo; |
- r = /*warning:INVALID_ASSIGNMENT*/nn; |
- r = /*warning:INVALID_ASSIGNMENT*/nnn; |
- |
- o = /*warning:DOWN_CAST_COMPOSITE*/r; |
- o = o; |
- o = /*warning:INVALID_ASSIGNMENT*/n; |
- o = /*warning:INVALID_ASSIGNMENT*/rr; |
- o = /*warning:INVALID_ASSIGNMENT*/ro; |
- o = /*warning:INVALID_ASSIGNMENT*/rn; |
- o = oo; |
- o = /*warning:INVALID_ASSIGNMENT*/nn; |
- o = /*warning:INVALID_ASSIGNMENT*/nnn; |
- |
- n = /*warning:INVALID_ASSIGNMENT*/r; |
- n = /*warning:INVALID_ASSIGNMENT*/o; |
- n = n; |
- n = /*warning:INVALID_ASSIGNMENT*/rr; |
- n = /*warning:INVALID_ASSIGNMENT*/ro; |
- n = /*warning:INVALID_ASSIGNMENT*/rn; |
- n = /*warning:INVALID_ASSIGNMENT*/oo; |
- n = nn; |
- n = nnn; |
- |
- rr = /*warning:INVALID_ASSIGNMENT*/r; |
- rr = /*warning:INVALID_ASSIGNMENT*/o; |
- rr = /*warning:INVALID_ASSIGNMENT*/n; |
- rr = rr; |
- rr = ro; |
- rr = /*warning:INVALID_ASSIGNMENT*/rn; |
- rr = oo; |
- rr = /*warning:INVALID_ASSIGNMENT*/nn; |
- rr = /*warning:INVALID_ASSIGNMENT*/nnn; |
- |
- ro = /*warning:DOWN_CAST_COMPOSITE*/r; |
- ro = /*warning:INVALID_ASSIGNMENT*/o; |
- ro = /*warning:INVALID_ASSIGNMENT*/n; |
- ro = /*warning:DOWN_CAST_COMPOSITE*/rr; |
- ro = ro; |
- ro = /*warning:INVALID_ASSIGNMENT*/rn; |
- ro = oo; |
- ro = /*warning:INVALID_ASSIGNMENT*/nn; |
- ro = /*warning:INVALID_ASSIGNMENT*/nnn; |
- |
- rn = /*warning:DOWN_CAST_COMPOSITE*/r; |
- rn = /*warning:INVALID_ASSIGNMENT*/o; |
- rn = /*warning:INVALID_ASSIGNMENT*/n; |
- rn = /*warning:INVALID_ASSIGNMENT*/rr; |
- rn = /*warning:INVALID_ASSIGNMENT*/ro; |
- rn = rn; |
- rn = /*warning:INVALID_ASSIGNMENT*/oo; |
- rn = /*warning:INVALID_ASSIGNMENT*/nn; |
- rn = /*warning:INVALID_ASSIGNMENT*/nnn; |
- |
- oo = /*warning:DOWN_CAST_COMPOSITE*/r; |
- oo = /*warning:DOWN_CAST_COMPOSITE*/o; |
- oo = /*warning:INVALID_ASSIGNMENT*/n; |
- oo = /*warning:DOWN_CAST_COMPOSITE*/rr; |
- oo = /*warning:DOWN_CAST_COMPOSITE*/ro; |
- oo = /*warning:INVALID_ASSIGNMENT*/rn; |
- oo = oo; |
- oo = /*warning:INVALID_ASSIGNMENT*/nn; |
- oo = /*warning:INVALID_ASSIGNMENT*/nnn; |
- |
- nn = /*warning:INVALID_ASSIGNMENT*/r; |
- nn = /*warning:INVALID_ASSIGNMENT*/o; |
- nn = /*warning:DOWN_CAST_COMPOSITE*/n; |
- nn = /*warning:INVALID_ASSIGNMENT*/rr; |
- nn = /*warning:INVALID_ASSIGNMENT*/ro; |
- nn = /*warning:INVALID_ASSIGNMENT*/rn; |
- nn = /*warning:INVALID_ASSIGNMENT*/oo; |
- nn = nn; |
- nn = nnn; |
- |
- nnn = /*warning:INVALID_ASSIGNMENT*/r; |
- nnn = /*warning:INVALID_ASSIGNMENT*/o; |
- nnn = /*warning:DOWN_CAST_COMPOSITE*/n; |
- nnn = /*warning:INVALID_ASSIGNMENT*/rr; |
- nnn = /*warning:INVALID_ASSIGNMENT*/ro; |
- nnn = /*warning:INVALID_ASSIGNMENT*/rn; |
- nnn = /*warning:INVALID_ASSIGNMENT*/oo; |
- nnn = /*warning:DOWN_CAST_COMPOSITE*/nn; |
- nnn = nnn; |
- } |
- '''); |
- }); |
- |
- test('Function subtyping: objects with call methods', () { |
- checkFile(''' |
- |
- 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 = /*warning:INVALID_ASSIGNMENT*/new B(); |
- f = i2i; |
- f = /*severe:STATIC_TYPE_ERROR*/n2n; |
- f = /*warning:DOWN_CAST_COMPOSITE*/i2i as Object; |
- f = /*warning:DOWN_CAST_COMPOSITE*/n2n as Function; |
- } |
- { |
- N2N f; |
- f = /*warning:INVALID_ASSIGNMENT*/new A(); |
- f = new B(); |
- f = /*severe:STATIC_TYPE_ERROR*/i2i; |
- f = n2n; |
- f = /*warning:DOWN_CAST_COMPOSITE*/i2i as Object; |
- f = /*warning:DOWN_CAST_COMPOSITE*/n2n as Function; |
- } |
- { |
- A f; |
- f = new A(); |
- f = /*warning:INVALID_ASSIGNMENT*/new B(); |
- f = /*warning:INVALID_ASSIGNMENT*/i2i; |
- f = /*warning:INVALID_ASSIGNMENT*/n2n; |
- f = /*info:DOWN_CAST_IMPLICIT*/i2i as Object; |
- f = /*info:DOWN_CAST_IMPLICIT*/n2n as Function; |
- } |
- { |
- B f; |
- f = /*warning:INVALID_ASSIGNMENT*/new A(); |
- f = new B(); |
- f = /*warning:INVALID_ASSIGNMENT*/i2i; |
- f = /*warning:INVALID_ASSIGNMENT*/n2n; |
- f = /*info:DOWN_CAST_IMPLICIT*/i2i as Object; |
- f = /*info:DOWN_CAST_IMPLICIT*/n2n as Function; |
- } |
- { |
- Function f; |
- f = new A(); |
- f = new B(); |
- f = i2i; |
- f = n2n; |
- f = /*info:DOWN_CAST_IMPLICIT*/i2i as Object; |
- f = (n2n as Function); |
- } |
- } |
- '''); |
- }); |
- |
- test('void', () { |
- checkFile(''' |
- |
- class A { |
- void bar() => null; |
- void foo() => bar(); // allowed |
- } |
- '''); |
- }); |
- |
- test('uninferred closure', () { |
- checkFile(''' |
- typedef num Num2Num(num x); |
- void main() { |
- Num2Num g = /*info:INFERRED_TYPE_CLOSURE,severe:STATIC_TYPE_ERROR*/(int x) { return x; }; |
- print(g(42)); |
- } |
- '''); |
- }); |
- |
- test('subtype of universal type', () { |
- checkFile(''' |
- void main() { |
- nonGenericFn(x) => null; |
- { |
- /*=R*/ f/*<P, R>*/(/*=P*/ p) => null; |
- /*=T*/ g/*<S, T>*/(/*=S*/ s) => null; |
- |
- var local = f; |
- local = g; // valid |
- |
- // Non-generic function cannot subtype a generic one. |
- local = /*warning:INVALID_ASSIGNMENT*/(x) => null; |
- local = /*warning:INVALID_ASSIGNMENT*/nonGenericFn; |
- } |
- { |
- Iterable/*<R>*/ f/*<P, R>*/(List/*<P>*/ p) => null; |
- List/*<T>*/ g/*<S, T>*/(Iterable/*<S>*/ s) => null; |
- |
- var local = f; |
- local = g; // valid |
- |
- var local2 = g; |
- local = local2; |
- local2 = /*severe:STATIC_TYPE_ERROR*/f; |
- local2 = /*warning:DOWN_CAST_COMPOSITE*/local; |
- |
- // Non-generic function cannot subtype a generic one. |
- local = /*warning:INVALID_ASSIGNMENT*/(x) => null; |
- local = /*warning:INVALID_ASSIGNMENT*/nonGenericFn; |
- } |
- } |
- '''); |
- }); |
- }); |
- |
- test('Relaxed casts', () { |
- checkFile(''' |
- |
- 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; |
- lOfDs = new L(); // Reset type propagation. |
- } |
- { |
- lOfOs = mOfDs; |
- lOfOs = mOfOs; |
- lOfOs = mOfAs; |
- lOfOs = lOfDs; |
- lOfOs = lOfOs; |
- lOfOs = lOfAs; |
- lOfOs = new L<Object>(); // Reset type propagation. |
- } |
- { |
- lOfAs = /*warning:DOWN_CAST_COMPOSITE*/mOfDs; |
- lOfAs = /*warning:INVALID_ASSIGNMENT*/mOfOs; |
- lOfAs = mOfAs; |
- lOfAs = /*warning:DOWN_CAST_COMPOSITE*/lOfDs; |
- lOfAs = /*info:DOWN_CAST_IMPLICIT*/lOfOs; |
- lOfAs = lOfAs; |
- lOfAs = new L<A>(); // Reset type propagation. |
- } |
- { |
- mOfDs = mOfDs; |
- mOfDs = mOfOs; |
- mOfDs = mOfAs; |
- mOfDs = /*info:DOWN_CAST_IMPLICIT*/lOfDs; |
- mOfDs = /*info:DOWN_CAST_IMPLICIT*/lOfOs; |
- mOfDs = /*warning:DOWN_CAST_COMPOSITE*/lOfAs; |
- mOfDs = new M(); // Reset type propagation. |
- } |
- { |
- mOfOs = mOfDs; |
- mOfOs = mOfOs; |
- mOfOs = mOfAs; |
- mOfOs = /*info:DOWN_CAST_IMPLICIT*/lOfDs; |
- mOfOs = /*info:DOWN_CAST_IMPLICIT*/lOfOs; |
- mOfOs = /*warning:INVALID_ASSIGNMENT*/lOfAs; |
- mOfOs = new M<Object>(); // Reset type propagation. |
- } |
- { |
- mOfAs = /*warning:DOWN_CAST_COMPOSITE*/mOfDs; |
- mOfAs = /*info:DOWN_CAST_IMPLICIT*/mOfOs; |
- mOfAs = mOfAs; |
- mOfAs = /*warning:DOWN_CAST_COMPOSITE*/lOfDs; |
- mOfAs = /*info:DOWN_CAST_IMPLICIT*/lOfOs; |
- mOfAs = /*info:DOWN_CAST_IMPLICIT*/lOfAs; |
- } |
- } |
- '''); |
- }); |
- |
- test('Type checking literals', () { |
- checkFile(''' |
- test() { |
- num n = 3; |
- int i = 3; |
- String s = "hello"; |
- { |
- List<int> l = <int>[i]; |
- l = <int>[/*warning:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/s]; |
- l = <int>[/*info:DOWN_CAST_IMPLICIT*/n]; |
- l = <int>[i, /*info:DOWN_CAST_IMPLICIT*/n, /*warning:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/s]; |
- } |
- { |
- List l = /*info:INFERRED_TYPE_LITERAL*/[i]; |
- l = /*info:INFERRED_TYPE_LITERAL*/[s]; |
- l = /*info:INFERRED_TYPE_LITERAL*/[n]; |
- l = /*info:INFERRED_TYPE_LITERAL*/[i, n, s]; |
- } |
- { |
- Map<String, int> m = <String, int>{s: i}; |
- m = <String, int>{s: /*warning:MAP_VALUE_TYPE_NOT_ASSIGNABLE*/s}; |
- m = <String, int>{s: /*info:DOWN_CAST_IMPLICIT*/n}; |
- m = <String, int>{s: i, |
- s: /*info:DOWN_CAST_IMPLICIT*/n, |
- s: /*warning:MAP_VALUE_TYPE_NOT_ASSIGNABLE*/s}; |
- } |
- // TODO(leafp): We can't currently test for key errors since the |
- // error marker binds to the entire entry. |
- { |
- Map m = /*info:INFERRED_TYPE_LITERAL*/{s: i}; |
- m = /*info:INFERRED_TYPE_LITERAL*/{s: s}; |
- m = /*info:INFERRED_TYPE_LITERAL*/{s: n}; |
- m = /*info:INFERRED_TYPE_LITERAL*/ |
- {s: i, |
- s: n, |
- s: s}; |
- m = /*info:INFERRED_TYPE_LITERAL*/ |
- {i: s, |
- n: s, |
- s: s}; |
- } |
- } |
- '''); |
- }); |
- |
- test('casts in constant contexts', () { |
- checkFile(''' |
- class A { |
- static const num n = 3.0; |
- // The severe error is from constant evaluation where we know the |
- // concrete type. |
- static const int /*severe:VARIABLE_TYPE_MISMATCH*/i = /*info:ASSIGNMENT_CAST*/n; |
- final int fi; |
- const A(num a) : this.fi = /*info:DOWN_CAST_IMPLICIT*/a; |
- } |
- class B extends A { |
- const B(Object a) : super(/*info:DOWN_CAST_IMPLICIT*/a); |
- } |
- void foo(Object o) { |
- var a = const A(/*info:DOWN_CAST_IMPLICIT, severe:CONST_WITH_NON_CONSTANT_ARGUMENT, severe:INVALID_CONSTANT*/o); |
- } |
- '''); |
- }); |
- |
- test('casts in conditionals', () { |
- checkFile(''' |
- main() { |
- bool b = true; |
- num x = b ? 1 : 2.3; |
- int y = /*info:ASSIGNMENT_CAST*/b ? 1 : 2.3; |
- String z = !b ? "hello" : null; |
- z = b ? null : "hello"; |
- } |
- '''); |
- }); |
- |
- // This is a regression test for https://github.com/dart-lang/sdk/issues/25071 |
- test('unbound redirecting constructor', () { |
- checkFile(''' |
- class Foo { |
- Foo() : /*severe:REDIRECT_GENERATIVE_TO_MISSING_CONSTRUCTOR*/this.init(); |
- } |
- '''); |
- }); |
- |
- test('redirecting constructor', () { |
- checkFile(''' |
- class A { |
- A(A x) {} |
- A.two() : this(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3); |
- } |
- '''); |
- }); |
- |
- test('super constructor', () { |
- checkFile(''' |
- class A { A(A x) {} } |
- class B extends A { |
- B() : super(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3); |
- } |
- '''); |
- }); |
- |
- test('factory constructor downcast', () { |
- checkFile(r''' |
- class Animal { |
- Animal(); |
- factory Animal.cat() => new Cat(); |
- } |
- |
- class Cat extends Animal {} |
- |
- void main() { |
- Cat c = /*info:ASSIGNMENT_CAST*/new Animal.cat(); |
- c = /*severe:STATIC_TYPE_ERROR*/new Animal(); |
- }'''); |
- }); |
- |
- test('field/field override', () { |
- checkFile(''' |
- 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:INVALID_FIELD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE*/A f1; // invalid for getter |
- /*severe:INVALID_FIELD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE*/C f2; // invalid for setter |
- /*severe:INVALID_FIELD_OVERRIDE*/var f3; |
- /*severe:INVALID_FIELD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE*/dynamic f4; |
- } |
- |
- class Child2 implements Base { |
- /*severe:INVALID_METHOD_OVERRIDE*/A f1; // invalid for getter |
- /*severe:INVALID_METHOD_OVERRIDE*/C f2; // invalid for setter |
- var f3; |
- /*severe:INVALID_METHOD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE*/dynamic f4; |
- } |
- '''); |
- }); |
- |
- test('private override', () { |
+ // Dynamic cast. |
+ await for (String /*info:DYNAMIC_CAST*/s in new Stream<dynamic>()) {} |
+ |
+ // Identity cast. |
+ await for (String s in new Stream<String>()) {} |
+ |
+ // Untyped. |
+ await for (var s in new Stream<String>()) {} |
+ |
+ // Downcast. |
+ await for (int /*info:DOWN_CAST_IMPLICIT*/i in new Stream<num>()) {} |
+} |
+'''); |
+ } |
+ |
+ void test_awaitForInCastsSupertypeSequenceToStream() { |
+ checkFile(''' |
+main() async { |
+ dynamic d; |
+ await for (var i in /*info:DYNAMIC_CAST*/d) {} |
+ |
+ Object o; |
+ await for (var i in /*info:DOWN_CAST_IMPLICIT*/o) {} |
+} |
+'''); |
+ } |
+ |
+ void test_binaryAndIndexOperators() { |
+ checkFile(''' |
+class A { |
+ A operator *(B b) => null; |
+ A operator /(B b) => null; |
+ A operator ~/(B b) => null; |
+ A operator %(B b) => null; |
+ A operator +(B b) => null; |
+ A operator -(B b) => null; |
+ A operator <<(B b) => null; |
+ A operator >>(B b) => null; |
+ A operator &(B b) => null; |
+ A operator ^(B b) => null; |
+ A operator |(B b) => null; |
+ A operator[](B b) => null; |
+} |
+ |
+class B { |
+ A operator -(B b) => null; |
+} |
+ |
+foo() => new A(); |
+ |
+test() { |
+ A a = new A(); |
+ B b = new B(); |
+ var c = foo(); |
+ a = a * b; |
+ a = a * /*info:DYNAMIC_CAST*/c; |
+ a = a / b; |
+ a = a ~/ b; |
+ a = a % b; |
+ a = a + b; |
+ a = a + /*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/a; |
+ a = a - b; |
+ b = /*warning:INVALID_ASSIGNMENT*/b - b; |
+ a = a << b; |
+ a = a >> b; |
+ a = a & b; |
+ a = a ^ b; |
+ a = a | b; |
+ c = (/*info:DYNAMIC_INVOKE*/c + b); |
+ |
+ String x = 'hello'; |
+ int y = 42; |
+ x = x + x; |
+ x = x + /*info:DYNAMIC_CAST*/c; |
+ x = x + /*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/y; |
+ |
+ bool p = true; |
+ p = p && p; |
+ p = p && /*info:DYNAMIC_CAST*/c; |
+ p = (/*info:DYNAMIC_CAST*/c) && p; |
+ p = (/*info:DYNAMIC_CAST*/c) && /*info:DYNAMIC_CAST*/c; |
+ p = /*warning:NON_BOOL_OPERAND*/y && p; |
+ p = c == y; |
+ |
+ a = a[b]; |
+ a = a[/*info:DYNAMIC_CAST*/c]; |
+ c = (/*info:DYNAMIC_INVOKE*/c[b]); |
+ a[/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/y]; |
+} |
+'''); |
+ } |
+ |
+ void test_castsInConditions() { |
+ checkFile(''' |
+main() { |
+ bool b = true; |
+ num x = b ? 1 : 2.3; |
+ int y = /*info:ASSIGNMENT_CAST*/b ? 1 : 2.3; |
+ String z = !b ? "hello" : null; |
+ z = b ? null : "hello"; |
+} |
+'''); |
+ } |
+ |
+ void test_castsInConstantContexts() { |
+ checkFile(''' |
+class A { |
+ static const num n = 3.0; |
+ // The severe error is from constant evaluation where we know the |
+ // concrete type. |
+ static const int /*severe:VARIABLE_TYPE_MISMATCH*/i = /*info:ASSIGNMENT_CAST*/n; |
+ final int fi; |
+ const A(num a) : this.fi = /*info:DOWN_CAST_IMPLICIT*/a; |
+} |
+class B extends A { |
+ const B(Object a) : super(/*info:DOWN_CAST_IMPLICIT*/a); |
+} |
+void foo(Object o) { |
+ var a = const A(/*info:DOWN_CAST_IMPLICIT, severe:CONST_WITH_NON_CONSTANT_ARGUMENT, severe:INVALID_CONSTANT*/o); |
+} |
+'''); |
+ } |
+ |
+ void test_classOverrideOfGrandInterface_interfaceOfAbstractSuperclass() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class I1 { |
+ m(A a); |
+} |
+abstract class Base implements I1 {} |
+ |
+class T1 extends Base { |
+ /*severe:INVALID_METHOD_OVERRIDE*/m( |
+ /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
+} |
+'''); |
+ } |
+ |
+ void test_classOverrideOfGrandInterface_interfaceOfConcreteSuperclass() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class I1 { |
+ m(A a); |
+} |
+ |
+class /*warning:NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE*/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(/*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
+} |
+'''); |
+ } |
+ |
+ void test_classOverrideOfGrandInterface_interfaceOfInterfaceOfChild() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class I1 { |
+ m(A a); |
+} |
+abstract class I2 implements I1 {} |
+ |
+class T1 implements I2 { |
+ /*severe:INVALID_METHOD_OVERRIDE*/m( |
+ /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
+} |
+'''); |
+ } |
+ |
+ void test_classOverrideOfGrandInterface_mixinOfInterfaceOfChild() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class M1 { |
+ m(A a); |
+} |
+abstract class I2 extends Object with M1 {} |
+ |
+class T1 implements I2 { |
+ /*severe:INVALID_METHOD_OVERRIDE*/m( |
+ /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
+} |
+'''); |
+ } |
+ |
+ void test_classOverrideOfGrandInterface_superclassOfInterfaceOfChild() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class I1 { |
+ m(A a); |
+} |
+abstract class I2 extends I1 {} |
+ |
+class T1 implements I2 { |
+ /*severe:INVALID_METHOD_OVERRIDE*/m( |
+ /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
+} |
+'''); |
+ } |
+ |
+ void test_compoundAssignments() { |
+ checkFile(''' |
+class A { |
+ A operator *(B b) => null; |
+ A operator /(B b) => null; |
+ A operator ~/(B b) => null; |
+ A operator %(B b) => null; |
+ A operator +(B b) => null; |
+ A operator -(B b) => null; |
+ A operator <<(B b) => null; |
+ A operator >>(B b) => null; |
+ A operator &(B b) => null; |
+ A operator ^(B b) => null; |
+ A operator |(B b) => null; |
+ D operator [](B index) => null; |
+ void operator []=(B index, D value) => null; |
+} |
+ |
+class B { |
+ A operator -(B b) => null; |
+} |
+ |
+class D { |
+ D operator +(D d) => null; |
+} |
+ |
+foo() => new A(); |
+ |
+test() { |
+ int x = 0; |
+ x += 5; |
+ /*severe:STATIC_TYPE_ERROR*/x += 3.14; |
+ |
+ double y = 0.0; |
+ y += 5; |
+ y += 3.14; |
+ |
+ num z = 0; |
+ z += 5; |
+ z += 3.14; |
+ |
+ x = /*info:DOWN_CAST_IMPLICIT*/x + z; |
+ x += /*info:DOWN_CAST_IMPLICIT*/z; |
+ y = y + z; |
+ y += z; |
+ |
+ dynamic w = 42; |
+ x += /*info:DYNAMIC_CAST*/w; |
+ y += /*info:DYNAMIC_CAST*/w; |
+ z += /*info:DYNAMIC_CAST*/w; |
+ |
+ A a = new A(); |
+ B b = new B(); |
+ var c = foo(); |
+ a = a * b; |
+ a *= b; |
+ a *= /*info:DYNAMIC_CAST*/c; |
+ a /= b; |
+ a ~/= b; |
+ a %= b; |
+ a += b; |
+ a += /*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/a; |
+ a -= b; |
+ /*severe:STATIC_TYPE_ERROR*/b -= /*warning:INVALID_ASSIGNMENT*/b; |
+ a <<= b; |
+ a >>= b; |
+ a &= b; |
+ a ^= b; |
+ a |= b; |
+ /*info:DYNAMIC_INVOKE*/c += b; |
+ |
+ var d = new D(); |
+ a[b] += d; |
+ a[/*info:DYNAMIC_CAST*/c] += d; |
+ a[/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/z] += d; |
+ a[b] += /*info:DYNAMIC_CAST*/c; |
+ a[b] += /*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/z; |
+ /*info:DYNAMIC_INVOKE,info:DYNAMIC_INVOKE*/c[b] += d; |
+} |
+'''); |
+ } |
+ |
+ void test_constructors() { |
+ checkFile(''' |
+const num z = 25; |
+Object obj = "world"; |
+ |
+class A { |
+ int x; |
+ String y; |
+ |
+ A(this.x) : this.y = /*warning:FIELD_INITIALIZER_NOT_ASSIGNABLE*/42; |
+ |
+ A.c1(p): this.x = /*info:DOWN_CAST_IMPLICIT*/z, this.y = /*info:DYNAMIC_CAST*/p; |
+ |
+ A.c2(this.x, this.y); |
+ |
+ A.c3(/*severe:INVALID_PARAMETER_DECLARATION*/num this.x, String this.y); |
+} |
+ |
+class B extends A { |
+ B() : super(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello"); |
+ |
+ B.c2(int x, String y) : super.c2(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/y, |
+ /*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/x); |
+ |
+ B.c3(num x, Object y) : super.c3(x, /*info:DOWN_CAST_IMPLICIT*/y); |
+} |
+ |
+void main() { |
+ A a = new A.c2(/*info:DOWN_CAST_IMPLICIT*/z, /*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/z); |
+ var b = new B.c2(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello", /*info:DOWN_CAST_IMPLICIT*/obj); |
+} |
+'''); |
+ } |
+ |
+ void test_conversionAndDynamicInvoke() { |
addFile( |
''' |
- import 'main.dart' as main; |
- |
- class Base { |
- var f1; |
- var _f2; |
- var _f3; |
- get _f4 => null; |
- |
- int _m1() => null; |
- } |
- |
- class GrandChild extends main.Child { |
- /*severe:INVALID_FIELD_OVERRIDE*/var _f2; |
- /*severe:INVALID_FIELD_OVERRIDE*/var _f3; |
- var _f4; |
- |
- /*severe:INVALID_METHOD_OVERRIDE*/String |
- /*warning:INVALID_METHOD_OVERRIDE_RETURN_TYPE*/_m1() => null; |
- } |
- ''', |
+dynamic toString = (int x) => x + 42; |
+dynamic hashCode = "hello"; |
+''', |
name: '/helper.dart'); |
checkFile(''' |
- import 'helper.dart' as helper; |
- |
- class Child extends helper.Base { |
- /*severe:INVALID_FIELD_OVERRIDE*/var f1; |
- var _f2; |
- var _f4; |
- |
- String _m1() => null; |
- } |
- '''); |
- }); |
- |
- test('getter/getter override', () { |
- checkFile(''' |
- 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:INVALID_METHOD_OVERRIDE*/A get f1 => null; |
- C get f2 => null; |
- get f3 => null; |
- /*severe:INVALID_METHOD_OVERRIDE*/dynamic get f4 => null; |
- } |
- '''); |
- }); |
- |
- test('field/getter override', () { |
- checkFile(''' |
- 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:INVALID_FIELD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE*/A get f1 => null; |
- /*severe:INVALID_FIELD_OVERRIDE*/C get f2 => null; |
- /*severe:INVALID_FIELD_OVERRIDE*/get f3 => null; |
- /*severe:INVALID_FIELD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE*/dynamic get f4 => null; |
- } |
- |
- class /*warning:NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FOUR*/Child2 implements Base { |
- /*severe:INVALID_METHOD_OVERRIDE*/A get f1 => null; |
- C get f2 => null; |
- get f3 => null; |
- /*severe:INVALID_METHOD_OVERRIDE*/dynamic get f4 => null; |
- } |
- '''); |
- }); |
- |
- test('setter/setter override', () { |
- checkFile(''' |
- 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:INVALID_METHOD_OVERRIDE*/void set f2(C value) {} |
- void set f3(value) {} |
- /*severe:INVALID_METHOD_OVERRIDE*/void set f4(dynamic value) {} |
- set f5(B value) {} |
- } |
- '''); |
- }); |
- |
- test('field/setter override', () { |
- checkFile(''' |
- 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 { |
- /*severe:INVALID_FIELD_OVERRIDE*/B get f1 => null; |
- /*severe:INVALID_FIELD_OVERRIDE*/B get f2 => null; |
- /*severe:INVALID_FIELD_OVERRIDE*/B get f3 => null; |
- /*severe:INVALID_FIELD_OVERRIDE*/B get f4 => null; |
- /*severe:INVALID_FIELD_OVERRIDE*/B get f5 => null; |
- |
- /*severe:INVALID_FIELD_OVERRIDE*/void set f1(A value) {} |
- /*severe:INVALID_FIELD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE*/void set f2(C value) {} |
- /*severe:INVALID_FIELD_OVERRIDE*/void set f3(value) {} |
- /*severe:INVALID_FIELD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE*/void set f4(dynamic value) {} |
- /*severe:INVALID_FIELD_OVERRIDE*/set f5(B value) {} |
- } |
- |
- class Child2 implements 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:INVALID_METHOD_OVERRIDE*/void set f2(C value) {} |
- void set f3(value) {} |
- /*severe:INVALID_METHOD_OVERRIDE*/void set f4(dynamic value) {} |
- set f5(B value) {} |
- } |
- '''); |
- }); |
- |
- test('method override', () { |
- checkFile(''' |
- class A {} |
- class B extends A {} |
- class C extends B {} |
- |
- class Base { |
- B m1(B a) => null; |
- B m2(B a) => null; |
- B m3(B a) => null; |
- B m4(B a) => null; |
- B m5(B a) => null; |
- B m6(B a) => null; |
- } |
- |
- class Child extends Base { |
- /*severe:INVALID_METHOD_OVERRIDE*/A m1(A value) => null; |
- /*severe:INVALID_METHOD_OVERRIDE*/C m2(C value) => null; |
- /*severe:INVALID_METHOD_OVERRIDE*/A m3(C value) => null; |
- C m4(A value) => null; |
- m5(value) => null; |
- /*severe:INVALID_METHOD_OVERRIDE*/dynamic m6(dynamic value) => null; |
- } |
- '''); |
- }); |
- |
- test('method override, fuzzy arrows', () { |
- checkFile(''' |
- abstract class A { |
- bool operator ==(Object object); |
- } |
- |
- class B implements A {} |
- |
- |
- class F { |
- void f(x) {} |
- void g(int x) {} |
- } |
- |
- class G extends F { |
- /*severe:INVALID_METHOD_OVERRIDE*/void f(int x) {} |
- void g(dynamic x) {} |
- } |
- |
- class H implements F { |
- /*severe:INVALID_METHOD_OVERRIDE*/void f(int x) {} |
- void g(dynamic x) {} |
- } |
- |
- '''); |
- }); |
- |
- test('getter override, fuzzy arrows', () { |
- checkFile(''' |
- typedef void ToVoid<T>(T x); |
- class F { |
- ToVoid<dynamic> get f => null; |
- ToVoid<int> get g => null; |
- } |
- |
- class G extends F { |
- ToVoid<int> get f => null; |
- /*severe:INVALID_METHOD_OVERRIDE*/ToVoid<dynamic> get g => null; |
- } |
- |
- class H implements F { |
- ToVoid<int> get f => null; |
- /*severe:INVALID_METHOD_OVERRIDE*/ToVoid<dynamic> get g => null; |
- } |
- '''); |
- }); |
- |
- test('setter override, fuzzy arrows', () { |
- checkFile(''' |
- typedef void ToVoid<T>(T x); |
- class F { |
- void set f(ToVoid<dynamic> x) {} |
- void set g(ToVoid<int> x) {} |
- void set h(dynamic x) {} |
- void set i(int x) {} |
- } |
- |
- class G extends F { |
- /*severe:INVALID_METHOD_OVERRIDE*/void set f(ToVoid<int> x) {} |
- void set g(ToVoid<dynamic> x) {} |
- void set h(int x) {} |
- /*severe:INVALID_METHOD_OVERRIDE*/void set i(dynamic x) {} |
- } |
- |
- class H implements F { |
- /*severe:INVALID_METHOD_OVERRIDE*/void set f(ToVoid<int> x) {} |
- void set g(ToVoid<dynamic> x) {} |
- void set h(int x) {} |
- /*severe:INVALID_METHOD_OVERRIDE*/void set i(dynamic x) {} |
- } |
- '''); |
- }); |
- |
- test('field override, fuzzy arrows', () { |
- checkFile(''' |
- typedef void ToVoid<T>(T x); |
- class F { |
- final ToVoid<dynamic> f = null; |
- final ToVoid<int> g = null; |
- } |
- |
- class G extends F { |
- /*severe:INVALID_FIELD_OVERRIDE*/final ToVoid<int> f = null; |
- /*severe:INVALID_FIELD_OVERRIDE, severe:INVALID_METHOD_OVERRIDE*/final ToVoid<dynamic> g = null; |
- } |
- |
- class H implements F { |
- final ToVoid<int> f = null; |
- /*severe:INVALID_METHOD_OVERRIDE*/final ToVoid<dynamic> g = null; |
- } |
- '''); |
- }); |
- |
- test('generic class method override', () { |
- checkFile(''' |
- class A {} |
- class B extends A {} |
- |
- class Base<T extends B> { |
- T foo() => null; |
- } |
- |
- class Derived<S extends A> extends Base<B> { |
- /*severe:INVALID_METHOD_OVERRIDE*/S |
- /*warning:INVALID_METHOD_OVERRIDE_RETURN_TYPE*/foo() => null; |
- } |
- |
- class Derived2<S extends B> extends Base<B> { |
- S foo() => null; |
- } |
- '''); |
- }); |
- |
- test('generic method override', () { |
- checkFile(''' |
- class Future<T> { |
- /*=S*/ then/*<S>*/(/*=S*/ onValue(T t)) => null; |
- } |
- |
- class DerivedFuture<T> extends Future<T> { |
- /*=S*/ then/*<S>*/(/*=S*/ onValue(T t)) => null; |
- } |
- |
- class DerivedFuture2<A> extends Future<A> { |
- /*=B*/ then/*<B>*/(/*=B*/ onValue(A a)) => null; |
- } |
- |
- class DerivedFuture3<T> extends Future<T> { |
- /*=S*/ then/*<S>*/(Object onValue(T t)) => null; |
- } |
- |
- class DerivedFuture4<A> extends Future<A> { |
- /*=B*/ then/*<B>*/(Object onValue(A a)) => null; |
- } |
- '''); |
- }); |
- |
- test('generic function wrong number of arguments', () { |
- checkFile(r''' |
- /*=T*/ foo/*<T>*/(/*=T*/ x, /*=T*/ y) => x; |
- /*=T*/ bar/*<T>*/({/*=T*/ x, /*=T*/ y}) => x; |
- |
- main() { |
- String x; |
- // resolving these shouldn't crash. |
- foo/*warning:EXTRA_POSITIONAL_ARGUMENTS*/(1, 2, 3); |
- x = foo/*warning:EXTRA_POSITIONAL_ARGUMENTS*/('1', '2', '3'); |
- foo/*warning:NOT_ENOUGH_REQUIRED_ARGUMENTS*/(1); |
- x = foo/*warning:NOT_ENOUGH_REQUIRED_ARGUMENTS*/('1'); |
- x = /*info:DYNAMIC_CAST*/foo/*warning:EXTRA_POSITIONAL_ARGUMENTS*/(1, 2, 3); |
- x = /*info:DYNAMIC_CAST*/foo/*warning:NOT_ENOUGH_REQUIRED_ARGUMENTS*/(1); |
- |
- // named arguments |
- bar(y: 1, x: 2, /*warning:UNDEFINED_NAMED_PARAMETER*/z: 3); |
- x = bar(/*warning:UNDEFINED_NAMED_PARAMETER*/z: '1', x: '2', y: '3'); |
- bar(y: 1); |
- x = bar(x: '1', /*warning:UNDEFINED_NAMED_PARAMETER*/z: 42); |
- x = /*info:DYNAMIC_CAST*/bar(y: 1, x: 2, /*warning:UNDEFINED_NAMED_PARAMETER*/z: 3); |
- x = /*info:DYNAMIC_CAST*/bar(x: 1); |
- } |
- '''); |
- }); |
- |
- test('type promotion from dynamic', () { |
- checkFile(r''' |
- f() { |
- dynamic x; |
- if (x is int) { |
- int y = x; |
- String z = /*warning:INVALID_ASSIGNMENT*/x; |
- } |
- } |
- g() { |
- Object x; |
- if (x is int) { |
- int y = x; |
- String z = /*warning:INVALID_ASSIGNMENT*/x; |
- } |
- } |
- '''); |
- }); |
- |
- test('unary operators', () { |
- checkFile(''' |
- class A { |
- A operator ~() => null; |
- A operator +(int x) => null; |
- A operator -(int x) => null; |
- A operator -() => null; |
- } |
- |
- foo() => new A(); |
- |
- test() { |
- A a = new A(); |
- var c = foo(); |
- dynamic d; |
- |
- ~a; |
- (/*info:DYNAMIC_INVOKE*/~d); |
- |
- !/*warning:NON_BOOL_NEGATION_EXPRESSION*/a; |
- !/*info:DYNAMIC_CAST*/d; |
- |
- -a; |
- (/*info:DYNAMIC_INVOKE*/-d); |
- |
- ++a; |
- --a; |
- (/*info:DYNAMIC_INVOKE*/++d); |
- (/*info:DYNAMIC_INVOKE*/--d); |
- |
- a++; |
- a--; |
- (/*info:DYNAMIC_INVOKE*/d++); |
- (/*info:DYNAMIC_INVOKE*/d--); |
- }'''); |
- }); |
- |
- test('binary and index operators', () { |
- checkFile(''' |
- class A { |
- A operator *(B b) => null; |
- A operator /(B b) => null; |
- A operator ~/(B b) => null; |
- A operator %(B b) => null; |
- A operator +(B b) => null; |
- A operator -(B b) => null; |
- A operator <<(B b) => null; |
- A operator >>(B b) => null; |
- A operator &(B b) => null; |
- A operator ^(B b) => null; |
- A operator |(B b) => null; |
- A operator[](B b) => null; |
- } |
- |
- class B { |
- A operator -(B b) => null; |
- } |
- |
- foo() => new A(); |
- |
- test() { |
- A a = new A(); |
- B b = new B(); |
- var c = foo(); |
- a = a * b; |
- a = a * /*info:DYNAMIC_CAST*/c; |
- a = a / b; |
- a = a ~/ b; |
- a = a % b; |
- a = a + b; |
- a = a + /*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/a; |
- a = a - b; |
- b = /*warning:INVALID_ASSIGNMENT*/b - b; |
- a = a << b; |
- a = a >> b; |
- a = a & b; |
- a = a ^ b; |
- a = a | b; |
- c = (/*info:DYNAMIC_INVOKE*/c + b); |
- |
- String x = 'hello'; |
- int y = 42; |
- x = x + x; |
- x = x + /*info:DYNAMIC_CAST*/c; |
- x = x + /*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/y; |
- |
- bool p = true; |
- p = p && p; |
- p = p && /*info:DYNAMIC_CAST*/c; |
- p = (/*info:DYNAMIC_CAST*/c) && p; |
- p = (/*info:DYNAMIC_CAST*/c) && /*info:DYNAMIC_CAST*/c; |
- p = /*warning:NON_BOOL_OPERAND*/y && p; |
- p = c == y; |
- |
- a = a[b]; |
- a = a[/*info:DYNAMIC_CAST*/c]; |
- c = (/*info:DYNAMIC_INVOKE*/c[b]); |
- a[/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/y]; |
- } |
- '''); |
- }); |
- |
- test('null coalescing operator', () { |
- checkFile(''' |
- class A {} |
- class C<T> {} |
- main() { |
- A a, b; |
- a ??= new A(); |
- b = b ?? new A(); |
- |
- // downwards inference |
- C<int> c, d; |
- c ??= /*info:INFERRED_TYPE_ALLOCATION*/new C(); |
- d = d ?? /*info:INFERRED_TYPE_ALLOCATION*/new C(); |
- } |
- '''); |
- }); |
- |
- test('compound assignments', () { |
- checkFile(''' |
- class A { |
- A operator *(B b) => null; |
- A operator /(B b) => null; |
- A operator ~/(B b) => null; |
- A operator %(B b) => null; |
- A operator +(B b) => null; |
- A operator -(B b) => null; |
- A operator <<(B b) => null; |
- A operator >>(B b) => null; |
- A operator &(B b) => null; |
- A operator ^(B b) => null; |
- A operator |(B b) => null; |
- D operator [](B index) => null; |
- void operator []=(B index, D value) => null; |
- } |
- |
- class B { |
- A operator -(B b) => null; |
- } |
- |
- class D { |
- D operator +(D d) => null; |
- } |
- |
- foo() => new A(); |
- |
- test() { |
- int x = 0; |
- x += 5; |
- /*severe:STATIC_TYPE_ERROR*/x += 3.14; |
- |
- double y = 0.0; |
- y += 5; |
- y += 3.14; |
- |
- num z = 0; |
- z += 5; |
- z += 3.14; |
- |
- x = /*info:DOWN_CAST_IMPLICIT*/x + z; |
- x += /*info:DOWN_CAST_IMPLICIT*/z; |
- y = y + z; |
- y += z; |
- |
- dynamic w = 42; |
- x += /*info:DYNAMIC_CAST*/w; |
- y += /*info:DYNAMIC_CAST*/w; |
- z += /*info:DYNAMIC_CAST*/w; |
- |
- A a = new A(); |
- B b = new B(); |
- var c = foo(); |
- a = a * b; |
- a *= b; |
- a *= /*info:DYNAMIC_CAST*/c; |
- a /= b; |
- a ~/= b; |
- a %= b; |
- a += b; |
- a += /*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/a; |
- a -= b; |
- /*severe:STATIC_TYPE_ERROR*/b -= /*warning:INVALID_ASSIGNMENT*/b; |
- a <<= b; |
- a >>= b; |
- a &= b; |
- a ^= b; |
- a |= b; |
- /*info:DYNAMIC_INVOKE*/c += b; |
- |
- var d = new D(); |
- a[b] += d; |
- a[/*info:DYNAMIC_CAST*/c] += d; |
- a[/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/z] += d; |
- a[b] += /*info:DYNAMIC_CAST*/c; |
- a[b] += /*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/z; |
- /*info:DYNAMIC_INVOKE,info:DYNAMIC_INVOKE*/c[b] += d; |
- } |
- '''); |
- }); |
- |
- test('super call placement', () { |
- checkFile(''' |
- 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:INVALID_SUPER_INVOCATION*/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(); |
- '''); |
- }); |
- |
- test('for loop variable', () { |
- checkFile(''' |
- foo() { |
- for (int i = 0; i < 10; i++) { |
- i = /*warning:INVALID_ASSIGNMENT*/"hi"; |
- } |
- } |
- bar() { |
- for (var i = 0; i < 10; i++) { |
- int j = i + 1; |
- } |
- } |
- '''); |
- }); |
- |
- test('loadLibrary', () { |
- addFile('''library lib1;''', name: '/lib1.dart'); |
+import 'helper.dart' as helper; |
+ |
+class A { |
+ String x = "hello world"; |
+ |
+ void baz1(y) { x + /*info:DYNAMIC_CAST*/y; } |
+ static baz2(y) => /*info:DYNAMIC_INVOKE*/y + y; |
+} |
+ |
+void foo(String str) { |
+ print(str); |
+} |
+ |
+class B { |
+ String toString([int arg]) => arg.toString(); |
+} |
+ |
+void bar(a) { |
+ foo(/*info:DYNAMIC_CAST,info:DYNAMIC_INVOKE*/a.x); |
+} |
+ |
+baz() => new B(); |
+ |
+typedef DynFun(x); |
+typedef StrFun(String x); |
+ |
+var bar1 = bar; |
+ |
+void main() { |
+ var a = new A(); |
+ bar(a); |
+ (/*info:DYNAMIC_INVOKE*/bar1(a)); |
+ var b = bar; |
+ (/*info:DYNAMIC_INVOKE*/b(a)); |
+ var f1 = foo; |
+ f1("hello"); |
+ dynamic f2 = foo; |
+ (/*info:DYNAMIC_INVOKE*/f2("hello")); |
+ DynFun f3 = foo; |
+ (/*info:DYNAMIC_INVOKE*/f3("hello")); |
+ (/*info:DYNAMIC_INVOKE*/f3(42)); |
+ StrFun f4 = foo; |
+ f4("hello"); |
+ a.baz1("hello"); |
+ var b1 = a.baz1; |
+ (/*info:DYNAMIC_INVOKE*/b1("hello")); |
+ A.baz2("hello"); |
+ var b2 = A.baz2; |
+ (/*info:DYNAMIC_INVOKE*/b2("hello")); |
+ |
+ dynamic a1 = new B(); |
+ (/*info:DYNAMIC_INVOKE*/a1./*info:UNDEFINED_GETTER*/x); |
+ a1.toString(); |
+ (/*info:DYNAMIC_INVOKE*/a1.toString(42)); |
+ var toStringClosure = a1.toString; |
+ (/*info:DYNAMIC_INVOKE*/a1.toStringClosure()); |
+ (/*info:DYNAMIC_INVOKE*/a1.toStringClosure(42)); |
+ (/*info:DYNAMIC_INVOKE*/a1.toStringClosure("hello")); |
+ a1.hashCode; |
+ |
+ dynamic toString = () => null; |
+ (/*info:DYNAMIC_INVOKE*/toString()); |
+ |
+ (/*info:DYNAMIC_INVOKE*/helper.toString()); |
+ var toStringClosure2 = helper.toString; |
+ (/*info:DYNAMIC_INVOKE*/toStringClosure2()); |
+ int hashCode = /*info:DYNAMIC_CAST*/helper.hashCode; |
+ |
+ baz().toString(); |
+ baz().hashCode; |
+} |
+'''); |
+ } |
+ |
+ void test_dynamicInvocation() { |
+ checkFile(''' |
+typedef dynamic A(dynamic x); |
+class B { |
+ int call(int x) => x; |
+ double col(double x) => x; |
+} |
+void main() { |
+ { |
+ B f = new B(); |
+ int x; |
+ double y; |
+ x = f(3); |
+ x = /*warning:INVALID_ASSIGNMENT*/f.col(3.0); |
+ y = /*warning:INVALID_ASSIGNMENT*/f(3); |
+ y = f.col(3.0); |
+ f(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3.0); |
+ f.col(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3); |
+ } |
+ { |
+ Function f = new B(); |
+ int x; |
+ double y; |
+ x = /*info:DYNAMIC_CAST, info:DYNAMIC_INVOKE*/f(3); |
+ x = /*info:DYNAMIC_CAST, info:DYNAMIC_INVOKE, info:INVALID_ASSIGNMENT*/f.col(3.0); |
+ y = /*info:DYNAMIC_CAST, info:DYNAMIC_INVOKE*/f(3); |
+ y = /*info:DYNAMIC_CAST, info:DYNAMIC_INVOKE*/f.col(3.0); |
+ /*info:DYNAMIC_INVOKE*/f(3.0); |
+ // Through type propagation, we know f is actually a B, hence the |
+ // hint. |
+ /*info:DYNAMIC_INVOKE*/f.col(/*info:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3); |
+ } |
+ { |
+ A f = new B(); |
+ int x; |
+ double y; |
+ x = /*info:DYNAMIC_CAST, info:DYNAMIC_INVOKE*/f(3); |
+ y = /*info:DYNAMIC_CAST, info:DYNAMIC_INVOKE*/f(3); |
+ /*info:DYNAMIC_INVOKE*/f(3.0); |
+ } |
+ { |
+ dynamic g = new B(); |
+ /*info:DYNAMIC_INVOKE*/g.call(/*info:ARGUMENT_TYPE_NOT_ASSIGNABLE*/32.0); |
+ /*info:DYNAMIC_INVOKE*/g.col(42.0); |
+ /*info:DYNAMIC_INVOKE*/g.foo(42.0); |
+ /*info:DYNAMIC_INVOKE*/g./*info:UNDEFINED_GETTER*/x; |
+ A f = new B(); |
+ /*info:DYNAMIC_INVOKE*/f.col(42.0); |
+ /*info:DYNAMIC_INVOKE*/f.foo(42.0); |
+ /*info:DYNAMIC_INVOKE*/f./*warning:UNDEFINED_GETTER*/x; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_factoryConstructorDowncast() { |
checkFile(r''' |
- import 'lib1.dart' deferred as lib1; |
- import 'dart:async' show Future; |
- main() { |
- Future f = lib1.loadLibrary(); |
- }'''); |
- }); |
- |
- group('invalid overrides', () { |
- test('child override', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- class Base { |
- A f; |
- } |
- |
- class T1 extends Base { |
- /*warning:MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE, severe:INVALID_FIELD_OVERRIDE, severe:INVALID_METHOD_OVERRIDE*/B get |
- /*warning:INVALID_GETTER_OVERRIDE_RETURN_TYPE*/f => null; |
- } |
- |
- class T2 extends Base { |
- /*warning:MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE, severe:INVALID_FIELD_OVERRIDE, severe:INVALID_METHOD_OVERRIDE*/set f( |
- /*warning:INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE*/B b) => null; |
- } |
- |
- class T3 extends Base { |
- /*severe:INVALID_FIELD_OVERRIDE, severe:INVALID_METHOD_OVERRIDE*/final B |
- /*warning:FINAL_NOT_INITIALIZED, warning:INVALID_GETTER_OVERRIDE_RETURN_TYPE*/f; |
- } |
- class T4 extends Base { |
- // two: one for the getter one for the setter. |
- /*severe:INVALID_FIELD_OVERRIDE, severe:INVALID_METHOD_OVERRIDE, severe:INVALID_METHOD_OVERRIDE*/B |
- /*warning:INVALID_GETTER_OVERRIDE_RETURN_TYPE, warning:INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE*/f; |
- } |
- |
- class /*warning:NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE*/T5 implements Base { |
- /*warning:MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE, severe:INVALID_METHOD_OVERRIDE*/B get |
- /*warning:INVALID_GETTER_OVERRIDE_RETURN_TYPE*/f => null; |
- } |
- |
- class /*warning:NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE*/T6 implements Base { |
- /*warning:MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE, severe:INVALID_METHOD_OVERRIDE*/set f( |
- /*warning:INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE*/B b) => null; |
- } |
- |
- class /*warning:NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE*/T7 implements Base { |
- /*severe:INVALID_METHOD_OVERRIDE*/final B |
- /*warning:INVALID_GETTER_OVERRIDE_RETURN_TYPE*/f = null; |
- } |
- class T8 implements Base { |
- // two: one for the getter one for the setter. |
- /*severe:INVALID_METHOD_OVERRIDE, severe:INVALID_METHOD_OVERRIDE*/B |
- /*warning:INVALID_GETTER_OVERRIDE_RETURN_TYPE, warning:INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE*/f; |
- } |
- '''); |
- }); |
- |
- test('child override 2', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- class Base { |
- m(A a) {} |
- } |
- |
- class Test extends Base { |
- /*severe:INVALID_METHOD_OVERRIDE*/m( |
- /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
- } |
- '''); |
- }); |
- test('grandchild override', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- class Grandparent { |
- m(A a) {} |
- int x; |
- } |
- class Parent extends Grandparent { |
- } |
- |
- class Test extends Parent { |
- /*severe:INVALID_METHOD_OVERRIDE*/m( |
- /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
- /*severe:INVALID_FIELD_OVERRIDE*/int x; |
- } |
- '''); |
- }); |
- |
- test('double override', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- class Grandparent { |
- m(A a) {} |
- } |
- class Parent extends Grandparent { |
- m(A a) {} |
- } |
- |
- class Test extends Parent { |
- // Reported only once |
- /*severe:INVALID_METHOD_OVERRIDE*/m( |
- /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
- } |
- '''); |
- }); |
- |
- test('double override 2', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- class Grandparent { |
- m(A a) {} |
- } |
- class Parent extends Grandparent { |
- /*severe:INVALID_METHOD_OVERRIDE*/m( |
- /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
- } |
- |
- class Test extends Parent { |
- m(B a) {} |
- } |
- '''); |
- }); |
- |
- test('mixin override to base', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- class Base { |
- m(A a) {} |
- int x; |
- } |
- |
- class M1 { |
- m(B a) {} |
- } |
- |
- class M2 { |
- int x; |
- } |
- |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 extends Base |
- with /*severe:INVALID_METHOD_OVERRIDE*/M1 {} |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T2 extends Base |
- with /*severe:INVALID_METHOD_OVERRIDE*/M1, /*severe:INVALID_FIELD_OVERRIDE*/M2 {} |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T3 extends Base |
- with /*severe:INVALID_FIELD_OVERRIDE*/M2, /*severe:INVALID_METHOD_OVERRIDE*/M1 {} |
- '''); |
- }); |
- |
- test('mixin override to mixin', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- class Base { |
- } |
- |
- class M1 { |
- m(B a) {} |
- int x; |
- } |
- |
- class M2 { |
- m(A a) {} |
- int x; |
- } |
- |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 extends Base |
- with M1, |
- /*severe:INVALID_METHOD_OVERRIDE,severe:INVALID_FIELD_OVERRIDE*/M2 {} |
- '''); |
- }); |
+class Animal { |
+ Animal(); |
+ factory Animal.cat() => new Cat(); |
+} |
- // 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. |
- test('no duplicate mixin override', () { |
- checkFile(''' |
- 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 /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 extends Base |
- with M1, /*severe:INVALID_METHOD_OVERRIDE*/M2, M3 {} |
- '''); |
- }); |
- |
- test('class override of interface', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class I { |
- m(A a); |
- } |
- |
- class T1 implements I { |
- /*severe:INVALID_METHOD_OVERRIDE*/m( |
- /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
- } |
- '''); |
- }); |
- |
- test('base class override to child interface', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class I { |
- m(A a); |
- } |
- |
- class Base { |
- m(B a) {} |
- } |
- |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
- /*severe:INVALID_METHOD_OVERRIDE*/extends Base implements I {} |
- '''); |
- }); |
- |
- test('mixin override of interface', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class I { |
- m(A a); |
- } |
- |
- class M { |
- m(B a) {} |
- } |
- |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
- extends Object with /*severe:INVALID_METHOD_OVERRIDE*/M |
- implements I {} |
- '''); |
- }); |
+class Cat extends Animal {} |
- // This is a case were it is incorrect to say that the base class |
- // incorrectly overrides the interface. |
- test('no errors if subclass correctly overrides base and interface', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- class Base { |
- m(A a) {} |
- } |
- |
- class I1 { |
- m(B a) {} |
- } |
- |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
- /*severe:INVALID_METHOD_OVERRIDE*/extends Base |
- implements I1 {} |
- |
- class T2 extends Base implements I1 { |
- m(a) {} |
- } |
- |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T3 |
- extends Object with /*severe:INVALID_METHOD_OVERRIDE*/Base |
- implements I1 {} |
- |
- class T4 extends Object with Base implements I1 { |
- m(a) {} |
- } |
- '''); |
- }); |
- }); |
- |
- group('class override of grand interface', () { |
- test('interface of interface of child', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class I1 { |
- m(A a); |
- } |
- abstract class I2 implements I1 {} |
- |
- class T1 implements I2 { |
- /*severe:INVALID_METHOD_OVERRIDE*/m( |
- /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
- } |
- '''); |
- }); |
- test('superclass of interface of child', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class I1 { |
- m(A a); |
- } |
- abstract class I2 extends I1 {} |
- |
- class T1 implements I2 { |
- /*severe:INVALID_METHOD_OVERRIDE*/m( |
- /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
- } |
- '''); |
- }); |
- test('mixin of interface of child', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class M1 { |
- m(A a); |
- } |
- abstract class I2 extends Object with M1 {} |
- |
- class T1 implements I2 { |
- /*severe:INVALID_METHOD_OVERRIDE*/m( |
- /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
- } |
- '''); |
- }); |
- test('interface of abstract superclass', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class I1 { |
- m(A a); |
- } |
- abstract class Base implements I1 {} |
- |
- class T1 extends Base { |
- /*severe:INVALID_METHOD_OVERRIDE*/m( |
- /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
- } |
- '''); |
- }); |
- test('interface of concrete superclass', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class I1 { |
- m(A a); |
- } |
- |
- class /*warning:NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE*/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(/*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
- } |
- '''); |
- }); |
- }); |
- |
- group('mixin override of grand interface', () { |
- test('interface of interface of child', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class I1 { |
- m(A a); |
- } |
- abstract class I2 implements I1 {} |
- |
- class M { |
- m(B a) {} |
- } |
- |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
- extends Object with /*severe:INVALID_METHOD_OVERRIDE*/M |
- implements I2 {} |
- '''); |
- }); |
- test('superclass of interface of child', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class I1 { |
- m(A a); |
- } |
- abstract class I2 extends I1 {} |
- |
- class M { |
- m(B a) {} |
- } |
- |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
- extends Object with /*severe:INVALID_METHOD_OVERRIDE*/M |
- implements I2 {} |
- '''); |
- }); |
- test('mixin of interface of child', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class M1 { |
- m(A a); |
- } |
- abstract class I2 extends Object with M1 {} |
- |
- class M { |
- m(B a) {} |
- } |
- |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
- extends Object with /*severe:INVALID_METHOD_OVERRIDE*/M |
- implements I2 {} |
- '''); |
- }); |
- test('interface of abstract superclass', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class I1 { |
- m(A a); |
- } |
- abstract class Base implements I1 {} |
- |
- class M { |
- m(B a) {} |
- } |
- |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 extends Base |
- with /*severe:INVALID_METHOD_OVERRIDE*/M {} |
- '''); |
- }); |
- test('interface of concrete superclass', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class I1 { |
- m(A a); |
- } |
- |
- class /*warning:NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE*/Base |
- implements I1 {} |
- |
- class M { |
- m(B a) {} |
- } |
- |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 extends Base |
- with M {} |
- '''); |
- }); |
- }); |
- |
- group('superclass override of grand interface', () { |
- test('interface of interface of child', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class I1 { |
- m(A a); |
- } |
- abstract class I2 implements I1 {} |
- |
- class Base { |
- m(B a) {} |
- } |
- |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
- /*severe:INVALID_METHOD_OVERRIDE*/extends Base implements I2 {} |
- '''); |
- }); |
- test('superclass of interface of child', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class I1 { |
- m(A a); |
- } |
- abstract class I2 extends I1 {} |
- |
- class Base { |
- m(B a) {} |
- } |
- |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
- /*severe:INVALID_METHOD_OVERRIDE*/extends Base |
- implements I2 {} |
- '''); |
- }); |
- test('mixin of interface of child', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class M1 { |
- m(A a); |
- } |
- abstract class I2 extends Object with M1 {} |
- |
- class Base { |
- m(B a) {} |
- } |
- |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
- /*severe:INVALID_METHOD_OVERRIDE*/extends Base |
- implements I2 {} |
- '''); |
- }); |
- test('interface of abstract superclass', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class I1 { |
- m(A a); |
- } |
- |
- abstract class Base implements I1 { |
- /*severe:INVALID_METHOD_OVERRIDE*/m( |
- /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/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:INVALID_METHOD_OVERRIDE*/m(B a) {} |
- } |
- '''); |
- }); |
- test('interface of concrete superclass', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class I1 { |
- m(A a); |
- } |
- |
- class Base implements I1 { |
- /*severe:INVALID_METHOD_OVERRIDE*/m( |
- /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
- } |
- |
- class T1 extends Base { |
- m(B a) {} |
- } |
- '''); |
- }); |
- }); |
- |
- group('no duplicate reports from overriding interfaces', () { |
- test('type overrides same method in multiple interfaces', () { |
- checkFile(''' |
- 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:INVALID_METHOD_OVERRIDE*/m( |
- /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
- } |
- '''); |
- }); |
- |
- test('type and base type override same method in interface', () { |
- checkFile(''' |
- 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:INVALID_METHOD_OVERRIDE*/m( |
- /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
- } |
- |
- // If there is no error in the class, we do report the error at |
- // the base class: |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T2 |
- /*severe:INVALID_METHOD_OVERRIDE*/extends Base |
- implements I1 {} |
- '''); |
- }); |
- |
- test('type and mixin override same method in interface', () { |
- checkFile(''' |
- class A {} |
- class B {} |
- |
- abstract class I1 { |
- m(A a); |
- } |
- |
- class M { |
- m(B a) {} |
- } |
- |
- class T1 extends Object with M implements I1 { |
- /*severe:INVALID_METHOD_OVERRIDE*/m( |
- /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
- } |
- |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T2 |
- extends Object with /*severe:INVALID_METHOD_OVERRIDE*/M |
- implements I1 {} |
- '''); |
- }); |
- |
- test('two grand types override same method in interface', () { |
- checkFile(''' |
- 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 /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
- /*severe:INVALID_METHOD_OVERRIDE*/extends Parent1 |
- implements I1 {} |
- class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T2 |
- /*severe:INVALID_METHOD_OVERRIDE*/extends Parent2 |
- implements I1 {} |
- '''); |
- }); |
- |
- test('two mixins override same method in interface', () { |
- checkFile(''' |
- 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 /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 extends Object |
- with /*severe:INVALID_METHOD_OVERRIDE*/M1, |
- /*severe:INVALID_METHOD_OVERRIDE*/M2 |
- implements I1 {} |
- '''); |
- }); |
- |
- test('base type and mixin override same method in interface', () { |
- checkFile(''' |
- 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 /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
- /*severe:INVALID_METHOD_OVERRIDE*/extends Base |
- with /*severe:INVALID_METHOD_OVERRIDE*/M |
- implements I1 {} |
- '''); |
- }); |
- }); |
- |
- test('invalid runtime checks', () { |
- checkFile(''' |
- 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:NON_GROUND_TYPE_CHECK_INFO*/foo is I2I; |
- b = /*info:NON_GROUND_TYPE_CHECK_INFO*/foo is D2I; |
- b = /*info:NON_GROUND_TYPE_CHECK_INFO*/foo is I2D; |
- b = foo is D2D; |
- |
- b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is II2I; |
- b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is DI2I; |
- b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is ID2I; |
- b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is II2D; |
- b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is DD2I; |
- b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is DI2D; |
- b = /*info:NON_GROUND_TYPE_CHECK_INFO*/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', () { |
- test('async', () { |
- checkFile(''' |
- import 'dart:async'; |
- import 'dart:math' show Random; |
- |
- dynamic x; |
- |
- foo1() async => x; |
- Future foo2() async => x; |
- Future<int> foo3() async => /*info:DYNAMIC_CAST*/x; |
- Future<int> foo4() async => new Future<int>.value(/*info:DYNAMIC_CAST*/x); |
- Future<int> foo5() async => |
- /*warning:RETURN_OF_INVALID_TYPE*/new Future<String>.value(/*info:DYNAMIC_CAST*/x); |
- |
- bar1() async { return x; } |
- Future bar2() async { return x; } |
- Future<int> bar3() async { return /*info:DYNAMIC_CAST*/x; } |
- Future<int> bar4() async { return new Future<int>.value(/*info:DYNAMIC_CAST*/x); } |
- Future<int> bar5() async { |
- return /*warning:RETURN_OF_INVALID_TYPE*/new Future<String>.value(/*info:DYNAMIC_CAST*/x); |
- } |
- |
- int y; |
- Future<int> z; |
- |
- baz() async { |
- int a = /*info:DYNAMIC_CAST*/await x; |
- int b = await y; |
- int c = await z; |
- String d = /*warning:INVALID_ASSIGNMENT*/await z; |
- } |
- |
- Future<bool> get issue_264 async { |
- await 42; |
- if (new Random().nextBool()) { |
- return true; |
- } else { |
- return new Future<bool>.value(false); |
- } |
- } |
- '''); |
- }); |
- |
- test('async*', () { |
- checkFile(''' |
- import 'dart:async'; |
- |
- dynamic x; |
- |
- bar1() async* { yield x; } |
- Stream bar2() async* { yield x; } |
- Stream<int> bar3() async* { yield /*info:DYNAMIC_CAST*/x; } |
- Stream<int> bar4() async* { yield /*warning:YIELD_OF_INVALID_TYPE*/new Stream<int>(); } |
- |
- baz1() async* { yield* /*info:DYNAMIC_CAST*/x; } |
- Stream baz2() async* { yield* /*info:DYNAMIC_CAST*/x; } |
- Stream<int> baz3() async* { yield* /*warning:DOWN_CAST_COMPOSITE*/x; } |
- Stream<int> baz4() async* { yield* new Stream<int>(); } |
- Stream<int> baz5() async* { yield* /*info:INFERRED_TYPE_ALLOCATION*/new Stream(); } |
- '''); |
- }); |
- |
- test('sync*', () { |
- checkFile(''' |
- dynamic x; |
- |
- bar1() sync* { yield x; } |
- Iterable bar2() sync* { yield x; } |
- Iterable<int> bar3() sync* { yield /*info:DYNAMIC_CAST*/x; } |
- Iterable<int> bar4() sync* { yield /*warning:YIELD_OF_INVALID_TYPE*/bar3(); } |
- |
- baz1() sync* { yield* /*info:DYNAMIC_CAST*/x; } |
- Iterable baz2() sync* { yield* /*info:DYNAMIC_CAST*/x; } |
- Iterable<int> baz3() sync* { yield* /*warning:DOWN_CAST_COMPOSITE*/x; } |
- Iterable<int> baz4() sync* { yield* bar3(); } |
- Iterable<int> baz5() sync* { yield* /*info:INFERRED_TYPE_ALLOCATION*/new List(); } |
- '''); |
- }); |
- }); |
+void main() { |
+ Cat c = /*info:ASSIGNMENT_CAST*/new Animal.cat(); |
+ c = /*severe:STATIC_TYPE_ERROR*/new Animal(); |
+}'''); |
+ } |
+ |
+ void test_fieldFieldOverride() { |
+ checkFile(''' |
+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:INVALID_FIELD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE*/A f1; // invalid for getter |
+ /*severe:INVALID_FIELD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE*/C f2; // invalid for setter |
+ /*severe:INVALID_FIELD_OVERRIDE*/var f3; |
+ /*severe:INVALID_FIELD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE*/dynamic f4; |
+} |
+ |
+class Child2 implements Base { |
+ /*severe:INVALID_METHOD_OVERRIDE*/A f1; // invalid for getter |
+ /*severe:INVALID_METHOD_OVERRIDE*/C f2; // invalid for setter |
+ var f3; |
+ /*severe:INVALID_METHOD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE*/dynamic f4; |
+} |
+'''); |
+ } |
+ |
+ void test_fieldGetterOverride() { |
+ checkFile(''' |
+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:INVALID_FIELD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE*/A get f1 => null; |
+ /*severe:INVALID_FIELD_OVERRIDE*/C get f2 => null; |
+ /*severe:INVALID_FIELD_OVERRIDE*/get f3 => null; |
+ /*severe:INVALID_FIELD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE*/dynamic get f4 => null; |
+} |
+ |
+class /*warning:NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FOUR*/Child2 implements Base { |
+ /*severe:INVALID_METHOD_OVERRIDE*/A get f1 => null; |
+ C get f2 => null; |
+ get f3 => null; |
+ /*severe:INVALID_METHOD_OVERRIDE*/dynamic get f4 => null; |
+} |
+'''); |
+ } |
+ |
+ void test_fieldOverride_fuzzyArrows() { |
+ checkFile(''' |
+typedef void ToVoid<T>(T x); |
+class F { |
+ final ToVoid<dynamic> f = null; |
+ final ToVoid<int> g = null; |
+} |
+ |
+class G extends F { |
+ /*severe:INVALID_FIELD_OVERRIDE*/final ToVoid<int> f = null; |
+ /*severe:INVALID_FIELD_OVERRIDE, severe:INVALID_METHOD_OVERRIDE*/final ToVoid<dynamic> g = null; |
+} |
+ |
+class H implements F { |
+ final ToVoid<int> f = null; |
+ /*severe:INVALID_METHOD_OVERRIDE*/final ToVoid<dynamic> g = null; |
+} |
+ '''); |
+ } |
+ |
+ void test_fieldSetterOverride() { |
+ checkFile(''' |
+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 { |
+ /*severe:INVALID_FIELD_OVERRIDE*/B get f1 => null; |
+ /*severe:INVALID_FIELD_OVERRIDE*/B get f2 => null; |
+ /*severe:INVALID_FIELD_OVERRIDE*/B get f3 => null; |
+ /*severe:INVALID_FIELD_OVERRIDE*/B get f4 => null; |
+ /*severe:INVALID_FIELD_OVERRIDE*/B get f5 => null; |
+ |
+ /*severe:INVALID_FIELD_OVERRIDE*/void set f1(A value) {} |
+ /*severe:INVALID_FIELD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE*/void set f2(C value) {} |
+ /*severe:INVALID_FIELD_OVERRIDE*/void set f3(value) {} |
+ /*severe:INVALID_FIELD_OVERRIDE,severe:INVALID_METHOD_OVERRIDE*/void set f4(dynamic value) {} |
+ /*severe:INVALID_FIELD_OVERRIDE*/set f5(B value) {} |
+} |
+ |
+class Child2 implements 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:INVALID_METHOD_OVERRIDE*/void set f2(C value) {} |
+ void set f3(value) {} |
+ /*severe:INVALID_METHOD_OVERRIDE*/void set f4(dynamic value) {} |
+ set f5(B value) {} |
+} |
+'''); |
+ } |
+ |
+ void test_forInCastsIterateElementToVariable() { |
+ checkFile(''' |
+main() { |
+ // Don't choke if sequence is not iterable. |
+ for (var i in /*warning:FOR_IN_OF_INVALID_TYPE*/1234) {} |
+ |
+ // Dynamic cast. |
+ for (String /*info:DYNAMIC_CAST*/s in <dynamic>[]) {} |
+ |
+ // Identity cast. |
+ for (String s in <String>[]) {} |
+ |
+ // Untyped. |
+ for (var s in <String>[]) {} |
+ |
+ // Downcast. |
+ for (int /*info:DOWN_CAST_IMPLICIT*/i in <num>[]) {} |
+} |
+'''); |
+ } |
+ |
+ void test_forInCastsSupertypeSequenceToIterate() { |
+ checkFile(''' |
+main() { |
+ dynamic d; |
+ for (var i in /*info:DYNAMIC_CAST*/d) {} |
+ |
+ Object o; |
+ for (var i in /*info:DOWN_CAST_IMPLICIT*/o) {} |
+} |
+'''); |
+ } |
+ |
+ void test_forLoopVariable() { |
+ checkFile(''' |
+foo() { |
+ for (int i = 0; i < 10; i++) { |
+ i = /*warning:INVALID_ASSIGNMENT*/"hi"; |
+ } |
+} |
+bar() { |
+ for (var i = 0; i < 10; i++) { |
+ int j = i + 1; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_functionModifiers_async() { |
+ checkFile(''' |
+import 'dart:async'; |
+import 'dart:math' show Random; |
+ |
+dynamic x; |
+ |
+foo1() async => x; |
+Future foo2() async => x; |
+Future<int> foo3() async => /*info:DYNAMIC_CAST*/x; |
+Future<int> foo4() async => new Future<int>.value(/*info:DYNAMIC_CAST*/x); |
+Future<int> foo5() async => |
+ /*warning:RETURN_OF_INVALID_TYPE*/new Future<String>.value(/*info:DYNAMIC_CAST*/x); |
+ |
+bar1() async { return x; } |
+Future bar2() async { return x; } |
+Future<int> bar3() async { return /*info:DYNAMIC_CAST*/x; } |
+Future<int> bar4() async { return new Future<int>.value(/*info:DYNAMIC_CAST*/x); } |
+Future<int> bar5() async { |
+ return /*warning:RETURN_OF_INVALID_TYPE*/new Future<String>.value(/*info:DYNAMIC_CAST*/x); |
+} |
+ |
+int y; |
+Future<int> z; |
+ |
+baz() async { |
+ int a = /*info:DYNAMIC_CAST*/await x; |
+ int b = await y; |
+ int c = await z; |
+ String d = /*warning:INVALID_ASSIGNMENT*/await z; |
+} |
+ |
+Future<bool> get issue_264 async { |
+ await 42; |
+ if (new Random().nextBool()) { |
+ return true; |
+ } else { |
+ return new Future<bool>.value(false); |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_functionModifiers_asyncStar() { |
+ checkFile(''' |
+import 'dart:async'; |
+ |
+dynamic x; |
+ |
+bar1() async* { yield x; } |
+Stream bar2() async* { yield x; } |
+Stream<int> bar3() async* { yield /*info:DYNAMIC_CAST*/x; } |
+Stream<int> bar4() async* { yield /*warning:YIELD_OF_INVALID_TYPE*/new Stream<int>(); } |
+ |
+baz1() async* { yield* /*info:DYNAMIC_CAST*/x; } |
+Stream baz2() async* { yield* /*info:DYNAMIC_CAST*/x; } |
+Stream<int> baz3() async* { yield* /*warning:DOWN_CAST_COMPOSITE*/x; } |
+Stream<int> baz4() async* { yield* new Stream<int>(); } |
+Stream<int> baz5() async* { yield* /*info:INFERRED_TYPE_ALLOCATION*/new Stream(); } |
+'''); |
+ } |
+ |
+ void test_functionModifiers_syncStar() { |
+ checkFile(''' |
+dynamic x; |
+ |
+bar1() sync* { yield x; } |
+Iterable bar2() sync* { yield x; } |
+Iterable<int> bar3() sync* { yield /*info:DYNAMIC_CAST*/x; } |
+Iterable<int> bar4() sync* { yield /*warning:YIELD_OF_INVALID_TYPE*/bar3(); } |
+ |
+baz1() sync* { yield* /*info:DYNAMIC_CAST*/x; } |
+Iterable baz2() sync* { yield* /*info:DYNAMIC_CAST*/x; } |
+Iterable<int> baz3() sync* { yield* /*warning:DOWN_CAST_COMPOSITE*/x; } |
+Iterable<int> baz4() sync* { yield* bar3(); } |
+Iterable<int> baz5() sync* { yield* /*info:INFERRED_TYPE_ALLOCATION*/new List(); } |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_classes() { |
+ checkFile(''' |
+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:DOWN_CAST_IMPLICIT*/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 = /*severe:STATIC_TYPE_ERROR*/top; |
+ f = left; |
+ f = /*severe:STATIC_TYPE_ERROR*/right; |
+ f = bot; |
+ } |
+ { |
+ Right f; |
+ f = /*severe:STATIC_TYPE_ERROR*/top; |
+ f = /*severe:STATIC_TYPE_ERROR*/left; |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Bot f; |
+ f = /*severe:STATIC_TYPE_ERROR*/top; |
+ f = /*severe:STATIC_TYPE_ERROR*/left; |
+ f = /*severe:STATIC_TYPE_ERROR*/right; |
+ f = bot; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_dynamic() { |
+ checkFile(''' |
+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 |
+ |
+void main() { |
+ Top top; |
+ Left left; |
+ Right right; |
+ Bottom bot; |
+ { |
+ Top f; |
+ f = top; |
+ f = left; |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Left f; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/top; |
+ f = left; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/right; |
+ f = bot; |
+ } |
+ { |
+ Right f; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/top; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/left; |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Bottom f; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/top; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/left; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/right; |
+ f = bot; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_dynamic_knownFunctions() { |
+ // Our lattice should look like this: |
+ // |
+ // |
+ // Bot -> Top |
+ // / \ |
+ // A -> Top Bot -> A |
+ // / \ / |
+ // Top -> Top A -> A |
+ // \ / |
+ // Top -> A |
+ // |
+ // Note that downcasts of known functions are promoted to |
+ // static type errors, since they cannot succeed. |
+ // This makes some of what look like downcasts turn into |
+ // type errors below. |
+ checkFile(''' |
+class A {} |
+ |
+typedef dynamic BotTop(dynamic x); |
+typedef dynamic ATop(A x); |
+typedef A BotA(dynamic x); |
+typedef A AA(A x); |
+typedef A TopA(Object x); |
+typedef dynamic TopTop(Object x); |
+ |
+dynamic aTop(A x) => x; |
+A aa(A x) => x; |
+dynamic topTop(dynamic x) => x; |
+A topA(dynamic x) => /*info:DYNAMIC_CAST*/x; |
+void apply/*<T>*/(/*=T*/ f0, /*=T*/ f1, /*=T*/ f2, |
+ /*=T*/ f3, /*=T*/ f4, /*=T*/ f5) {} |
+void main() { |
+ BotTop botTop; |
+ BotA botA; |
+ { |
+ BotTop f; |
+ f = topA; |
+ f = topTop; |
+ f = aa; |
+ f = aTop; |
+ f = botA; |
+ f = botTop; |
+ apply/*<BotTop>*/( |
+ topA, |
+ topTop, |
+ aa, |
+ aTop, |
+ botA, |
+ botTop |
+ ); |
+ apply/*<BotTop>*/( |
+ (dynamic x) => new A(), |
+ (dynamic x) => (x as Object), |
+ (A x) => x, |
+ (A x) => null, |
+ botA, |
+ botTop |
+ ); |
+ } |
+ { |
+ ATop f; |
+ f = topA; |
+ f = topTop; |
+ f = aa; |
+ f = aTop; |
+ f = /*warning:DOWN_CAST_COMPOSITE should be severe:STATIC_TYPE_ERROR*/botA; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/botTop; |
+ apply/*<ATop>*/( |
+ topA, |
+ topTop, |
+ aa, |
+ aTop, |
+ /*warning:DOWN_CAST_COMPOSITE should be severe:STATIC_TYPE_ERROR*/botA, |
+ /*warning:DOWN_CAST_COMPOSITE*/botTop |
+ ); |
+ apply/*<ATop>*/( |
+ (dynamic x) => new A(), |
+ (dynamic x) => (x as Object), |
+ (A x) => x, |
+ (A x) => null, |
+ /*warning:DOWN_CAST_COMPOSITE should be severe:STATIC_TYPE_ERROR*/botA, |
+ /*warning:DOWN_CAST_COMPOSITE*/botTop |
+ ); |
+ } |
+ { |
+ BotA f; |
+ f = topA; |
+ f = /*severe:STATIC_TYPE_ERROR*/topTop; |
+ f = aa; |
+ f = /*severe:STATIC_TYPE_ERROR*/aTop; |
+ f = botA; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/botTop; |
+ apply/*<BotA>*/( |
+ topA, |
+ /*severe:STATIC_TYPE_ERROR*/topTop, |
+ aa, |
+ /*severe:STATIC_TYPE_ERROR*/aTop, |
+ botA, |
+ /*warning:DOWN_CAST_COMPOSITE*/botTop |
+ ); |
+ apply/*<BotA>*/( |
+ (dynamic x) => new A(), |
+ /*severe:STATIC_TYPE_ERROR*/(dynamic x) => (x as Object), |
+ (A x) => x, |
+ /*severe:STATIC_TYPE_ERROR*/(A x) => (x as Object), |
+ botA, |
+ /*warning:DOWN_CAST_COMPOSITE*/botTop |
+ ); |
+ } |
+ { |
+ AA f; |
+ f = topA; |
+ f = /*severe:STATIC_TYPE_ERROR*/topTop; |
+ f = aa; |
+ f = /*severe:STATIC_TYPE_ERROR*/aTop; // known function |
+ f = /*warning:DOWN_CAST_COMPOSITE*/botA; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/botTop; |
+ apply/*<AA>*/( |
+ topA, |
+ /*severe:STATIC_TYPE_ERROR*/topTop, |
+ aa, |
+ /*severe:STATIC_TYPE_ERROR*/aTop, // known function |
+ /*warning:DOWN_CAST_COMPOSITE*/botA, |
+ /*warning:DOWN_CAST_COMPOSITE*/botTop |
+ ); |
+ apply/*<AA>*/( |
+ (dynamic x) => new A(), |
+ /*severe:STATIC_TYPE_ERROR*/(dynamic x) => (x as Object), |
+ (A x) => x, |
+ /*severe:STATIC_TYPE_ERROR*/(A x) => (x as Object), // known function |
+ /*warning:DOWN_CAST_COMPOSITE*/botA, |
+ /*warning:DOWN_CAST_COMPOSITE*/botTop |
+ ); |
+ } |
+ { |
+ TopTop f; |
+ f = topA; |
+ f = topTop; |
+ f = /*severe:STATIC_TYPE_ERROR*/aa; |
+ f = /*severe:STATIC_TYPE_ERROR*/aTop; // known function |
+ f = /*warning:DOWN_CAST_COMPOSITE should be severe:STATIC_TYPE_ERROR*/botA; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/botTop; |
+ apply/*<TopTop>*/( |
+ topA, |
+ topTop, |
+ /*severe:STATIC_TYPE_ERROR*/aa, |
+ /*severe:STATIC_TYPE_ERROR*/aTop, // known function |
+ /*warning:DOWN_CAST_COMPOSITE should be severe:STATIC_TYPE_ERROR*/botA, |
+ /*warning:DOWN_CAST_COMPOSITE*/botTop |
+ ); |
+ apply/*<TopTop>*/( |
+ (dynamic x) => new A(), |
+ (dynamic x) => (x as Object), |
+ /*severe:STATIC_TYPE_ERROR*/(A x) => x, |
+ /*severe:STATIC_TYPE_ERROR*/(A x) => (x as Object), // known function |
+ /*warning:DOWN_CAST_COMPOSITE should be severe:STATIC_TYPE_ERROR*/botA, |
+ /*warning:DOWN_CAST_COMPOSITE*/botTop |
+ ); |
+ } |
+ { |
+ TopA f; |
+ f = topA; |
+ f = /*severe:STATIC_TYPE_ERROR*/topTop; // known function |
+ f = /*severe:STATIC_TYPE_ERROR*/aa; // known function |
+ f = /*severe:STATIC_TYPE_ERROR*/aTop; // known function |
+ f = /*warning:DOWN_CAST_COMPOSITE*/botA; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/botTop; |
+ apply/*<TopA>*/( |
+ topA, |
+ /*severe:STATIC_TYPE_ERROR*/topTop, // known function |
+ /*severe:STATIC_TYPE_ERROR*/aa, // known function |
+ /*severe:STATIC_TYPE_ERROR*/aTop, // known function |
+ /*warning:DOWN_CAST_COMPOSITE*/botA, |
+ /*warning:DOWN_CAST_COMPOSITE*/botTop |
+ ); |
+ apply/*<TopA>*/( |
+ (dynamic x) => new A(), |
+ /*severe:STATIC_TYPE_ERROR*/(dynamic x) => (x as Object), // known function |
+ /*severe:STATIC_TYPE_ERROR*/(A x) => x, // known function |
+ /*severe:STATIC_TYPE_ERROR*/(A x) => (x as Object), // known function |
+ /*warning:DOWN_CAST_COMPOSITE*/botA, |
+ /*warning:DOWN_CAST_COMPOSITE*/botTop |
+ ); |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_dynamicFunctions_clasuresAreNotFuzzy() { |
+ // Regression test for |
+ // https://github.com/dart-lang/sdk/issues/26118 |
+ // https://github.com/dart-lang/sdk/issues/26156 |
+ checkFile(''' |
+void takesF(void f(int x)) {} |
+ |
+typedef void TakesInt(int x); |
+ |
+void update(_) {} |
+void updateOpt([_]) {} |
+void updateOptNum([num x]) {} |
+ |
+class A { |
+ TakesInt f; |
+ A(TakesInt g) { |
+ f = update; |
+ f = updateOpt; |
+ f = updateOptNum; |
+ } |
+ TakesInt g(bool a, bool b) { |
+ if (a) { |
+ return update; |
+ } else if (b) { |
+ return updateOpt; |
+ } else { |
+ return updateOptNum; |
+ } |
+ } |
+} |
+ |
+void test0() { |
+ takesF(update); |
+ takesF(updateOpt); |
+ takesF(updateOptNum); |
+ TakesInt f; |
+ f = update; |
+ f = updateOpt; |
+ f = updateOptNum; |
+ new A(update); |
+ new A(updateOpt); |
+ new A(updateOptNum); |
+} |
+ |
+void test1() { |
+ void takesF(f(int x)) => null; |
+ takesF((dynamic y) => 3); |
+} |
+ |
+void test2() { |
+ int x; |
+ int f/*<T>*/(/*=T*/ t, callback(/*=T*/ x)) { return 3; } |
+ f(x, (y) => 3); |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_functionLiteralVariance() { |
+ checkFile(''' |
+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 = /*severe:STATIC_TYPE_ERROR*/top; |
+ f = left; |
+ f = /*severe:STATIC_TYPE_ERROR*/right; |
+ f = bot; |
+ } |
+ { |
+ Function2<A, A> f; |
+ f = /*severe:STATIC_TYPE_ERROR*/top; |
+ f = /*severe:STATIC_TYPE_ERROR*/left; |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Function2<A, B> f; |
+ f = /*severe:STATIC_TYPE_ERROR*/top; |
+ f = /*severe:STATIC_TYPE_ERROR*/left; |
+ f = /*severe:STATIC_TYPE_ERROR*/right; |
+ f = bot; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_functionVariableVariance() { |
+ checkFile(''' |
+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:DOWN_CAST_COMPOSITE*/top; |
+ left = left; |
+ left = /*warning:DOWN_CAST_COMPOSITE*/right; // Should we reject this? |
+ left = bot; |
+ |
+ right = /*warning:DOWN_CAST_COMPOSITE*/top; |
+ right = /*warning:DOWN_CAST_COMPOSITE*/left; // Should we reject this? |
+ right = right; |
+ right = bot; |
+ |
+ bot = /*warning:DOWN_CAST_COMPOSITE*/top; |
+ bot = /*warning:DOWN_CAST_COMPOSITE*/left; |
+ bot = /*warning:DOWN_CAST_COMPOSITE*/right; |
+ bot = bot; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_higherOrderFunctionLiteral1() { |
+ checkFile(''' |
+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:DOWN_CAST_COMPOSITE*/f; |
+AToB bot(BToA 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 = /*severe:STATIC_TYPE_ERROR*/top; |
+ f = left; |
+ f = /*severe:STATIC_TYPE_ERROR*/right; |
+ f = bot; |
+ } |
+ { |
+ Function2<BToA, BToA> f; // Right |
+ f = /*severe:STATIC_TYPE_ERROR*/top; |
+ f = /*severe:STATIC_TYPE_ERROR*/left; |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Function2<BToA, AToB> f; // Bot |
+ f = bot; |
+ f = /*severe:STATIC_TYPE_ERROR*/left; |
+ f = /*severe:STATIC_TYPE_ERROR*/top; |
+ f = /*severe:STATIC_TYPE_ERROR*/left; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_higherOrderFunctionLiteral2() { |
+ checkFile(''' |
+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 |
+ |
+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:DOWN_CAST_COMPOSITE*/f; |
+Function2<A, B> bot(BToA f) => f as Function2<A, B>; |
+ |
+void main() { |
+ { |
+ Function2<AToB, BToA> f; // Top |
+ f = top; |
+ f = left; |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Function2<AToB, AToB> f; // Left |
+ f = /*severe:STATIC_TYPE_ERROR*/top; |
+ f = left; |
+ f = /*severe:STATIC_TYPE_ERROR*/right; |
+ f = bot; |
+ } |
+ { |
+ Function2<BToA, BToA> f; // Right |
+ f = /*severe:STATIC_TYPE_ERROR*/top; |
+ f = /*severe:STATIC_TYPE_ERROR*/left; |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Function2<BToA, AToB> f; // Bot |
+ f = bot; |
+ f = /*severe:STATIC_TYPE_ERROR*/left; |
+ f = /*severe:STATIC_TYPE_ERROR*/top; |
+ f = /*severe:STATIC_TYPE_ERROR*/left; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_higherOrderFunctionLiteral3() { |
+ checkFile(''' |
+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(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:DOWN_CAST_COMPOSITE*/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 = /*severe:STATIC_TYPE_ERROR*/top; |
+ f = left; |
+ f = /*severe:STATIC_TYPE_ERROR*/right; |
+ f = bot; |
+ } |
+ { |
+ Function2<BToA, BToA> f; // Right |
+ f = /*severe:STATIC_TYPE_ERROR*/top; |
+ f = /*severe:STATIC_TYPE_ERROR*/left; |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Function2<BToA, AToB> f; // Bot |
+ f = bot; |
+ f = /*severe:STATIC_TYPE_ERROR*/left; |
+ f = /*severe:STATIC_TYPE_ERROR*/top; |
+ f = /*severe:STATIC_TYPE_ERROR*/left; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_higherOrderFunctionVariables() { |
+ checkFile(''' |
+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:DOWN_CAST_COMPOSITE*/top; |
+ left = left; |
+ left = |
+ /*warning:DOWN_CAST_COMPOSITE should be severe:STATIC_TYPE_ERROR*/right; |
+ left = bot; |
+ |
+ right = /*warning:DOWN_CAST_COMPOSITE*/top; |
+ right = |
+ /*warning:DOWN_CAST_COMPOSITE should be severe:STATIC_TYPE_ERROR*/left; |
+ right = right; |
+ right = bot; |
+ |
+ bot = /*warning:DOWN_CAST_COMPOSITE*/top; |
+ bot = /*warning:DOWN_CAST_COMPOSITE*/left; |
+ bot = /*warning:DOWN_CAST_COMPOSITE*/right; |
+ bot = bot; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_instanceMethodVariance() { |
+ checkFile(''' |
+class A {} |
+class B extends A {} |
+ |
+class C { |
+ A top(B x) => x; |
+ B left(B x) => x; |
+ A right(A x) => x; |
+ B bot(A x) => x as B; |
+} |
+ |
+typedef T Function2<S, T>(S z); |
+ |
+void main() { |
+ C c = new C(); |
+ { |
+ Function2<B, A> f; |
+ f = c.top; |
+ f = c.left; |
+ f = c.right; |
+ f = c.bot; |
+ } |
+ { |
+ Function2<B, B> f; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/c.top; |
+ f = c.left; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/c.right; |
+ f = c.bot; |
+ } |
+ { |
+ Function2<A, A> f; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/c.top; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/c.left; |
+ f = c.right; |
+ f = c.bot; |
+ } |
+ { |
+ Function2<A, B> f; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/c.top; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/c.left; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/c.right; |
+ f = c.bot; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_intAndObject() { |
+ checkFile(''' |
+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 globalTop(int x) => x; |
+int globalLeft(int x) => x; |
+Object globalRight(Object x) => x; |
+int bot_(Object x) => /*info:DOWN_CAST_IMPLICIT*/x; |
+int globalBot(Object x) => x as int; |
+ |
+void main() { |
+ // Note: use locals so we only know the type, not that it's a specific |
+ // function declaration. (we can issue better errors in that case.) |
+ var top = globalTop; |
+ var left = globalLeft; |
+ var right = globalRight; |
+ var bot = globalBot; |
+ |
+ { // Check typedef equality |
+ Left f = left; |
+ Left2 g = f; |
+ } |
+ { |
+ Top f; |
+ f = top; |
+ f = left; |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Left f; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/top; |
+ f = left; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/right; // Should we reject this? |
+ f = bot; |
+ } |
+ { |
+ Right f; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/top; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/left; // Should we reject this? |
+ f = right; |
+ f = bot; |
+ } |
+ { |
+ Bot f; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/top; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/left; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/right; |
+ f = bot; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_namedAndOptionalParameters() { |
+ checkFile(''' |
+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 = /*warning:INVALID_ASSIGNMENT*/n; |
+ r = /*warning:INVALID_ASSIGNMENT*/rr; |
+ r = ro; |
+ r = rn; |
+ r = oo; |
+ r = /*warning:INVALID_ASSIGNMENT*/nn; |
+ r = /*warning:INVALID_ASSIGNMENT*/nnn; |
+ |
+ o = /*warning:DOWN_CAST_COMPOSITE*/r; |
+ o = o; |
+ o = /*warning:INVALID_ASSIGNMENT*/n; |
+ o = /*warning:INVALID_ASSIGNMENT*/rr; |
+ o = /*warning:INVALID_ASSIGNMENT*/ro; |
+ o = /*warning:INVALID_ASSIGNMENT*/rn; |
+ o = oo; |
+ o = /*warning:INVALID_ASSIGNMENT*/nn; |
+ o = /*warning:INVALID_ASSIGNMENT*/nnn; |
+ |
+ n = /*warning:INVALID_ASSIGNMENT*/r; |
+ n = /*warning:INVALID_ASSIGNMENT*/o; |
+ n = n; |
+ n = /*warning:INVALID_ASSIGNMENT*/rr; |
+ n = /*warning:INVALID_ASSIGNMENT*/ro; |
+ n = /*warning:INVALID_ASSIGNMENT*/rn; |
+ n = /*warning:INVALID_ASSIGNMENT*/oo; |
+ n = nn; |
+ n = nnn; |
+ |
+ rr = /*warning:INVALID_ASSIGNMENT*/r; |
+ rr = /*warning:INVALID_ASSIGNMENT*/o; |
+ rr = /*warning:INVALID_ASSIGNMENT*/n; |
+ rr = rr; |
+ rr = ro; |
+ rr = /*warning:INVALID_ASSIGNMENT*/rn; |
+ rr = oo; |
+ rr = /*warning:INVALID_ASSIGNMENT*/nn; |
+ rr = /*warning:INVALID_ASSIGNMENT*/nnn; |
+ |
+ ro = /*warning:DOWN_CAST_COMPOSITE*/r; |
+ ro = /*warning:INVALID_ASSIGNMENT*/o; |
+ ro = /*warning:INVALID_ASSIGNMENT*/n; |
+ ro = /*warning:DOWN_CAST_COMPOSITE*/rr; |
+ ro = ro; |
+ ro = /*warning:INVALID_ASSIGNMENT*/rn; |
+ ro = oo; |
+ ro = /*warning:INVALID_ASSIGNMENT*/nn; |
+ ro = /*warning:INVALID_ASSIGNMENT*/nnn; |
+ |
+ rn = /*warning:DOWN_CAST_COMPOSITE*/r; |
+ rn = /*warning:INVALID_ASSIGNMENT*/o; |
+ rn = /*warning:INVALID_ASSIGNMENT*/n; |
+ rn = /*warning:INVALID_ASSIGNMENT*/rr; |
+ rn = /*warning:INVALID_ASSIGNMENT*/ro; |
+ rn = rn; |
+ rn = /*warning:INVALID_ASSIGNMENT*/oo; |
+ rn = /*warning:INVALID_ASSIGNMENT*/nn; |
+ rn = /*warning:INVALID_ASSIGNMENT*/nnn; |
+ |
+ oo = /*warning:DOWN_CAST_COMPOSITE*/r; |
+ oo = /*warning:DOWN_CAST_COMPOSITE*/o; |
+ oo = /*warning:INVALID_ASSIGNMENT*/n; |
+ oo = /*warning:DOWN_CAST_COMPOSITE*/rr; |
+ oo = /*warning:DOWN_CAST_COMPOSITE*/ro; |
+ oo = /*warning:INVALID_ASSIGNMENT*/rn; |
+ oo = oo; |
+ oo = /*warning:INVALID_ASSIGNMENT*/nn; |
+ oo = /*warning:INVALID_ASSIGNMENT*/nnn; |
+ |
+ nn = /*warning:INVALID_ASSIGNMENT*/r; |
+ nn = /*warning:INVALID_ASSIGNMENT*/o; |
+ nn = /*warning:DOWN_CAST_COMPOSITE*/n; |
+ nn = /*warning:INVALID_ASSIGNMENT*/rr; |
+ nn = /*warning:INVALID_ASSIGNMENT*/ro; |
+ nn = /*warning:INVALID_ASSIGNMENT*/rn; |
+ nn = /*warning:INVALID_ASSIGNMENT*/oo; |
+ nn = nn; |
+ nn = nnn; |
+ |
+ nnn = /*warning:INVALID_ASSIGNMENT*/r; |
+ nnn = /*warning:INVALID_ASSIGNMENT*/o; |
+ nnn = /*warning:DOWN_CAST_COMPOSITE*/n; |
+ nnn = /*warning:INVALID_ASSIGNMENT*/rr; |
+ nnn = /*warning:INVALID_ASSIGNMENT*/ro; |
+ nnn = /*warning:INVALID_ASSIGNMENT*/rn; |
+ nnn = /*warning:INVALID_ASSIGNMENT*/oo; |
+ nnn = /*warning:DOWN_CAST_COMPOSITE*/nn; |
+ nnn = nnn; |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_objectsWithCallMethods() { |
+ checkFile(''' |
+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 = /*warning:INVALID_ASSIGNMENT*/new B(); |
+ f = i2i; |
+ f = /*severe:STATIC_TYPE_ERROR*/n2n; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/i2i as Object; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/n2n as Function; |
+ } |
+ { |
+ N2N f; |
+ f = /*warning:INVALID_ASSIGNMENT*/new A(); |
+ f = new B(); |
+ f = /*severe:STATIC_TYPE_ERROR*/i2i; |
+ f = n2n; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/i2i as Object; |
+ f = /*warning:DOWN_CAST_COMPOSITE*/n2n as Function; |
+ } |
+ { |
+ A f; |
+ f = new A(); |
+ f = /*warning:INVALID_ASSIGNMENT*/new B(); |
+ f = /*warning:INVALID_ASSIGNMENT*/i2i; |
+ f = /*warning:INVALID_ASSIGNMENT*/n2n; |
+ f = /*info:DOWN_CAST_IMPLICIT*/i2i as Object; |
+ f = /*info:DOWN_CAST_IMPLICIT*/n2n as Function; |
+ } |
+ { |
+ B f; |
+ f = /*warning:INVALID_ASSIGNMENT*/new A(); |
+ f = new B(); |
+ f = /*warning:INVALID_ASSIGNMENT*/i2i; |
+ f = /*warning:INVALID_ASSIGNMENT*/n2n; |
+ f = /*info:DOWN_CAST_IMPLICIT*/i2i as Object; |
+ f = /*info:DOWN_CAST_IMPLICIT*/n2n as Function; |
+ } |
+ { |
+ Function f; |
+ f = new A(); |
+ f = new B(); |
+ f = i2i; |
+ f = n2n; |
+ f = /*info:DOWN_CAST_IMPLICIT*/i2i as Object; |
+ f = (n2n as Function); |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_staticMethodVariance() { |
+ checkFile(''' |
+class A {} |
+class B extends A {} |
+ |
+class C { |
+ static A top(B x) => x; |
+ static B left(B x) => x; |
+ static A right(A x) => x; |
+ static B bot(A x) => x as B; |
+} |
+ |
+typedef T Function2<S, T>(S z); |
+ |
+void main() { |
+ { |
+ Function2<B, A> f; |
+ f = C.top; |
+ f = C.left; |
+ f = C.right; |
+ f = C.bot; |
+ } |
+ { |
+ Function2<B, B> f; |
+ f = /*severe:STATIC_TYPE_ERROR*/C.top; |
+ f = C.left; |
+ f = /*severe:STATIC_TYPE_ERROR*/C.right; |
+ f = C.bot; |
+ } |
+ { |
+ Function2<A, A> f; |
+ f = /*severe:STATIC_TYPE_ERROR*/C.top; |
+ f = /*severe:STATIC_TYPE_ERROR*/C.left; |
+ f = C.right; |
+ f = C.bot; |
+ } |
+ { |
+ Function2<A, B> f; |
+ f = /*severe:STATIC_TYPE_ERROR*/C.top; |
+ f = /*severe:STATIC_TYPE_ERROR*/C.left; |
+ f = /*severe:STATIC_TYPE_ERROR*/C.right; |
+ f = C.bot; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_subtypeOfUniversalType() { |
+ checkFile(''' |
+void main() { |
+ nonGenericFn(x) => null; |
+ { |
+ /*=R*/ f/*<P, R>*/(/*=P*/ p) => null; |
+ /*=T*/ g/*<S, T>*/(/*=S*/ s) => null; |
+ |
+ var local = f; |
+ local = g; // valid |
+ |
+ // Non-generic function cannot subtype a generic one. |
+ local = /*warning:INVALID_ASSIGNMENT*/(x) => null; |
+ local = /*warning:INVALID_ASSIGNMENT*/nonGenericFn; |
+ } |
+ { |
+ Iterable/*<R>*/ f/*<P, R>*/(List/*<P>*/ p) => null; |
+ List/*<T>*/ g/*<S, T>*/(Iterable/*<S>*/ s) => null; |
+ |
+ var local = f; |
+ local = g; // valid |
+ |
+ var local2 = g; |
+ local = local2; |
+ local2 = /*severe:STATIC_TYPE_ERROR*/f; |
+ local2 = /*warning:DOWN_CAST_COMPOSITE*/local; |
+ |
+ // Non-generic function cannot subtype a generic one. |
+ local = /*warning:INVALID_ASSIGNMENT*/(x) => null; |
+ local = /*warning:INVALID_ASSIGNMENT*/nonGenericFn; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_uninferredClosure() { |
+ checkFile(''' |
+typedef num Num2Num(num x); |
+void main() { |
+ Num2Num g = /*info:INFERRED_TYPE_CLOSURE,severe:STATIC_TYPE_ERROR*/(int x) { return x; }; |
+ print(g(42)); |
+} |
+'''); |
+ } |
+ |
+ void test_functionTypingAndSubtyping_void() { |
+ checkFile(''' |
+class A { |
+ void bar() => null; |
+ void foo() => bar(); // allowed |
+} |
+'''); |
+ } |
+ |
+ void test_genericClassMethodOverride() { |
+ checkFile(''' |
+class A {} |
+class B extends A {} |
+ |
+class Base<T extends B> { |
+ T foo() => null; |
+} |
+ |
+class Derived<S extends A> extends Base<B> { |
+ /*severe:INVALID_METHOD_OVERRIDE*/S |
+ /*warning:INVALID_METHOD_OVERRIDE_RETURN_TYPE*/foo() => null; |
+} |
+ |
+class Derived2<S extends B> extends Base<B> { |
+ S foo() => null; |
+} |
+'''); |
+ } |
+ |
+ void test_genericFunctionWrongNumberOfArguments() { |
+ checkFile(r''' |
+/*=T*/ foo/*<T>*/(/*=T*/ x, /*=T*/ y) => x; |
+/*=T*/ bar/*<T>*/({/*=T*/ x, /*=T*/ y}) => x; |
+ |
+main() { |
+ String x; |
+ // resolving these shouldn't crash. |
+ foo/*warning:EXTRA_POSITIONAL_ARGUMENTS*/(1, 2, 3); |
+ x = foo/*warning:EXTRA_POSITIONAL_ARGUMENTS*/('1', '2', '3'); |
+ foo/*warning:NOT_ENOUGH_REQUIRED_ARGUMENTS*/(1); |
+ x = foo/*warning:NOT_ENOUGH_REQUIRED_ARGUMENTS*/('1'); |
+ x = /*info:DYNAMIC_CAST*/foo/*warning:EXTRA_POSITIONAL_ARGUMENTS*/(1, 2, 3); |
+ x = /*info:DYNAMIC_CAST*/foo/*warning:NOT_ENOUGH_REQUIRED_ARGUMENTS*/(1); |
+ |
+ // named arguments |
+ bar(y: 1, x: 2, /*warning:UNDEFINED_NAMED_PARAMETER*/z: 3); |
+ x = bar(/*warning:UNDEFINED_NAMED_PARAMETER*/z: '1', x: '2', y: '3'); |
+ bar(y: 1); |
+ x = bar(x: '1', /*warning:UNDEFINED_NAMED_PARAMETER*/z: 42); |
+ x = /*info:DYNAMIC_CAST*/bar(y: 1, x: 2, /*warning:UNDEFINED_NAMED_PARAMETER*/z: 3); |
+ x = /*info:DYNAMIC_CAST*/bar(x: 1); |
+} |
+'''); |
+ } |
+ |
+ void test_genericMethodOverride() { |
+ checkFile(''' |
+class Future<T> { |
+ /*=S*/ then/*<S>*/(/*=S*/ onValue(T t)) => null; |
+} |
+ |
+class DerivedFuture<T> extends Future<T> { |
+ /*=S*/ then/*<S>*/(/*=S*/ onValue(T t)) => null; |
+} |
+ |
+class DerivedFuture2<A> extends Future<A> { |
+ /*=B*/ then/*<B>*/(/*=B*/ onValue(A a)) => null; |
+} |
+ |
+class DerivedFuture3<T> extends Future<T> { |
+ /*=S*/ then/*<S>*/(Object onValue(T t)) => null; |
+} |
+ |
+class DerivedFuture4<A> extends Future<A> { |
+ /*=B*/ then/*<B>*/(Object onValue(A a)) => null; |
+} |
+'''); |
+ } |
+ |
+ void test_getterGetterOverride() { |
+ checkFile(''' |
+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:INVALID_METHOD_OVERRIDE*/A get f1 => null; |
+ C get f2 => null; |
+ get f3 => null; |
+ /*severe:INVALID_METHOD_OVERRIDE*/dynamic get f4 => null; |
+} |
+'''); |
+ } |
+ |
+ void test_getterOverride_fuzzyArrows() { |
+ checkFile(''' |
+typedef void ToVoid<T>(T x); |
+ |
+class F { |
+ ToVoid<dynamic> get f => null; |
+ ToVoid<int> get g => null; |
+} |
+ |
+class G extends F { |
+ ToVoid<int> get f => null; |
+ /*severe:INVALID_METHOD_OVERRIDE*/ToVoid<dynamic> get g => null; |
+} |
+ |
+class H implements F { |
+ ToVoid<int> get f => null; |
+ /*severe:INVALID_METHOD_OVERRIDE*/ToVoid<dynamic> get g => null; |
+} |
+'''); |
+ } |
+ |
+ void test_ifForDoWhileStatementsUseBooleanConversion() { |
+ checkFile(''' |
+main() { |
+ dynamic dyn = 42; |
+ Object obj = 42; |
+ int i = 42; |
+ bool b = false; |
+ |
+ if (b) {} |
+ if (/*info:DYNAMIC_CAST*/dyn) {} |
+ if (/*info:DOWN_CAST_IMPLICIT*/obj) {} |
+ if (/*warning:NON_BOOL_CONDITION*/i) {} |
+ |
+ while (b) {} |
+ while (/*info:DYNAMIC_CAST*/dyn) {} |
+ while (/*info:DOWN_CAST_IMPLICIT*/obj) {} |
+ while (/*warning:NON_BOOL_CONDITION*/i) {} |
+ |
+ do {} while (b); |
+ do {} while (/*info:DYNAMIC_CAST*/dyn); |
+ do {} while (/*info:DOWN_CAST_IMPLICIT*/obj); |
+ do {} while (/*warning:NON_BOOL_CONDITION*/i); |
+ |
+ for (;b;) {} |
+ for (;/*info:DYNAMIC_CAST*/dyn;) {} |
+ for (;/*info:DOWN_CAST_IMPLICIT*/obj;) {} |
+ for (;/*warning:NON_BOOL_CONDITION*/i;) {} |
+} |
+'''); |
+ } |
+ |
+ void test_invalidOverrides_baseClassOverrideToChildInterface() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class I { |
+ m(A a); |
+} |
+ |
+class Base { |
+ m(B a) {} |
+} |
+ |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
+ /*severe:INVALID_METHOD_OVERRIDE*/extends Base implements I {} |
+'''); |
+ } |
+ |
+ void test_invalidOverrides_childOverride() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+class Base { |
+ A f; |
+} |
+ |
+class T1 extends Base { |
+ /*warning:MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE, severe:INVALID_FIELD_OVERRIDE, severe:INVALID_METHOD_OVERRIDE*/B get |
+ /*warning:INVALID_GETTER_OVERRIDE_RETURN_TYPE*/f => null; |
+} |
+ |
+class T2 extends Base { |
+ /*warning:MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE, severe:INVALID_FIELD_OVERRIDE, severe:INVALID_METHOD_OVERRIDE*/set f( |
+ /*warning:INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE*/B b) => null; |
+} |
+ |
+class T3 extends Base { |
+ /*severe:INVALID_FIELD_OVERRIDE, severe:INVALID_METHOD_OVERRIDE*/final B |
+ /*warning:FINAL_NOT_INITIALIZED, warning:INVALID_GETTER_OVERRIDE_RETURN_TYPE*/f; |
+} |
+class T4 extends Base { |
+ // two: one for the getter one for the setter. |
+ /*severe:INVALID_FIELD_OVERRIDE, severe:INVALID_METHOD_OVERRIDE, severe:INVALID_METHOD_OVERRIDE*/B |
+ /*warning:INVALID_GETTER_OVERRIDE_RETURN_TYPE, warning:INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE*/f; |
+} |
+ |
+class /*warning:NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE*/T5 implements Base { |
+ /*warning:MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE, severe:INVALID_METHOD_OVERRIDE*/B get |
+ /*warning:INVALID_GETTER_OVERRIDE_RETURN_TYPE*/f => null; |
+} |
+ |
+class /*warning:NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE*/T6 implements Base { |
+ /*warning:MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE, severe:INVALID_METHOD_OVERRIDE*/set f( |
+ /*warning:INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE*/B b) => null; |
+} |
+ |
+class /*warning:NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE*/T7 implements Base { |
+ /*severe:INVALID_METHOD_OVERRIDE*/final B |
+ /*warning:INVALID_GETTER_OVERRIDE_RETURN_TYPE*/f = null; |
+} |
+class T8 implements Base { |
+ // two: one for the getter one for the setter. |
+ /*severe:INVALID_METHOD_OVERRIDE, severe:INVALID_METHOD_OVERRIDE*/B |
+ /*warning:INVALID_GETTER_OVERRIDE_RETURN_TYPE, warning:INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE*/f; |
+} |
+'''); |
+ } |
+ |
+ void test_invalidOverrides_childOverride2() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+class Base { |
+ m(A a) {} |
+} |
+ |
+class Test extends Base { |
+ /*severe:INVALID_METHOD_OVERRIDE*/m( |
+ /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
+} |
+'''); |
+ } |
+ |
+ void test_invalidOverrides_classOverrideOfInterface() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class I { |
+ m(A a); |
+} |
+ |
+class T1 implements I { |
+ /*severe:INVALID_METHOD_OVERRIDE*/m( |
+ /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
+} |
+'''); |
+ } |
+ |
+ void test_invalidOverrides_doubleOverride() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+class Grandparent { |
+ m(A a) {} |
+} |
+class Parent extends Grandparent { |
+ m(A a) {} |
+} |
+ |
+class Test extends Parent { |
+ // Reported only once |
+ /*severe:INVALID_METHOD_OVERRIDE*/m( |
+ /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
+} |
+'''); |
+ } |
+ |
+ void test_invalidOverrides_doubleOverride2() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+class Grandparent { |
+ m(A a) {} |
+} |
+class Parent extends Grandparent { |
+ /*severe:INVALID_METHOD_OVERRIDE*/m( |
+ /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
+} |
+ |
+class Test extends Parent { |
+ m(B a) {} |
+} |
+'''); |
+ } |
+ |
+ void test_invalidOverrides_grandChildOverride() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+class Grandparent { |
+ m(A a) {} |
+ int x; |
+} |
+class Parent extends Grandparent { |
+} |
+ |
+class Test extends Parent { |
+ /*severe:INVALID_METHOD_OVERRIDE*/m( |
+ /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
+ /*severe:INVALID_FIELD_OVERRIDE*/int x; |
+} |
+'''); |
+ } |
+ |
+ void test_invalidOverrides_mixinOverrideOfInterface() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class I { |
+ m(A a); |
+} |
+ |
+class M { |
+ m(B a) {} |
+} |
+ |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
+ extends Object with /*severe:INVALID_METHOD_OVERRIDE*/M |
+ implements I {} |
+'''); |
+ } |
+ |
+ void test_invalidOverrides_mixinOverrideToBase() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+class Base { |
+ m(A a) {} |
+ int x; |
+} |
+ |
+class M1 { |
+ m(B a) {} |
+} |
+ |
+class M2 { |
+ int x; |
+} |
+ |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 extends Base |
+ with /*severe:INVALID_METHOD_OVERRIDE*/M1 {} |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T2 extends Base |
+ with /*severe:INVALID_METHOD_OVERRIDE*/M1, /*severe:INVALID_FIELD_OVERRIDE*/M2 {} |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T3 extends Base |
+ with /*severe:INVALID_FIELD_OVERRIDE*/M2, /*severe:INVALID_METHOD_OVERRIDE*/M1 {} |
+'''); |
+ } |
+ |
+ void test_invalidOverrides_mixinOverrideToMixin() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+class Base { |
+} |
+ |
+class M1 { |
+ m(B a) {} |
+ int x; |
+} |
+ |
+class M2 { |
+ m(A a) {} |
+ int x; |
+} |
+ |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 extends Base |
+ with M1, |
+ /*severe:INVALID_METHOD_OVERRIDE,severe:INVALID_FIELD_OVERRIDE*/M2 {} |
+'''); |
+ } |
+ |
+ void test_invalidOverrides_noDuplicateMixinOverride() { |
+ // 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. |
+ checkFile(''' |
+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 /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 extends Base |
+ with M1, /*severe:INVALID_METHOD_OVERRIDE*/M2, M3 {} |
+'''); |
+ } |
+ |
+ void |
+ test_invalidOverrides_noErrorsIfSubclassCorrectlyOverrideBaseAndInterface() { |
+ // This is a case were it is incorrect to say that the base class |
+ // incorrectly overrides the interface. |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+class Base { |
+ m(A a) {} |
+} |
+ |
+class I1 { |
+ m(B a) {} |
+} |
+ |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
+ /*severe:INVALID_METHOD_OVERRIDE*/extends Base |
+ implements I1 {} |
+ |
+class T2 extends Base implements I1 { |
+ m(a) {} |
+} |
+ |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T3 |
+ extends Object with /*severe:INVALID_METHOD_OVERRIDE*/Base |
+ implements I1 {} |
+ |
+class T4 extends Object with Base implements I1 { |
+ m(a) {} |
+} |
+'''); |
+ } |
+ |
+ void test_invalidRuntimeChecks() { |
+ checkFile(''' |
+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:NON_GROUND_TYPE_CHECK_INFO*/foo is I2I; |
+ b = /*info:NON_GROUND_TYPE_CHECK_INFO*/foo is D2I; |
+ b = /*info:NON_GROUND_TYPE_CHECK_INFO*/foo is I2D; |
+ b = foo is D2D; |
+ |
+ b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is II2I; |
+ b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is DI2I; |
+ b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is ID2I; |
+ b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is II2D; |
+ b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is DD2I; |
+ b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is DI2D; |
+ b = /*info:NON_GROUND_TYPE_CHECK_INFO*/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; |
+} |
+'''); |
+ } |
+ |
+ void test_leastUpperBounds() { |
+ checkFile(''' |
+typedef T Returns<T>(); |
+ |
+// regression test for https://github.com/dart-lang/sdk/issues/26094 |
+class A <S extends Returns<S>, T extends Returns<T>> { |
+ int test(bool b) { |
+ S s; |
+ T t; |
+ if (b) { |
+ return /*warning:RETURN_OF_INVALID_TYPE*/b ? s : t; |
+ } else { |
+ return /*warning:RETURN_OF_INVALID_TYPE*/s ?? t; |
+ } |
+ } |
+} |
+ |
+class B<S, T extends S> { |
+ T t; |
+ S s; |
+ int test(bool b) { |
+ return /*warning:RETURN_OF_INVALID_TYPE*/b ? t : s; |
+ } |
+} |
+ |
+class C { |
+ // Check that the least upper bound of two types with the same |
+ // class but different type arguments produces the pointwise |
+ // least upper bound of the type arguments |
+ int test1(bool b) { |
+ List<int> li; |
+ List<double> ld; |
+ return /*warning:RETURN_OF_INVALID_TYPE*/b ? li : ld; |
+ } |
+ // TODO(leafp): This case isn't handled yet. This test checks |
+ // the case where two related classes are instantiated with related |
+ // but different types. |
+ Iterable<num> test2(bool b) { |
+ List<int> li; |
+ Iterable<double> id; |
+ int x = |
+ /*info:ASSIGNMENT_CAST should be warning:INVALID_ASSIGNMENT*/ |
+ b ? li : id; |
+ return /*warning:DOWN_CAST_COMPOSITE should be pass*/b ? li : id; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_loadLibrary() { |
+ addFile('''library lib1;''', name: '/lib1.dart'); |
+ checkFile(r''' |
+import 'lib1.dart' deferred as lib1; |
+import 'dart:async' show Future; |
+main() { |
+ Future f = lib1.loadLibrary(); |
+}'''); |
+ } |
+ |
+ void test_methodOverride() { |
+ checkFile(''' |
+class A {} |
+class B extends A {} |
+class C extends B {} |
+ |
+class Base { |
+ B m1(B a) => null; |
+ B m2(B a) => null; |
+ B m3(B a) => null; |
+ B m4(B a) => null; |
+ B m5(B a) => null; |
+ B m6(B a) => null; |
+} |
+ |
+class Child extends Base { |
+ /*severe:INVALID_METHOD_OVERRIDE*/A m1(A value) => null; |
+ /*severe:INVALID_METHOD_OVERRIDE*/C m2(C value) => null; |
+ /*severe:INVALID_METHOD_OVERRIDE*/A m3(C value) => null; |
+ C m4(A value) => null; |
+ m5(value) => null; |
+ /*severe:INVALID_METHOD_OVERRIDE*/dynamic m6(dynamic value) => null; |
+} |
+'''); |
+ } |
+ |
+ void test_methodOverride_fuzzyArrows() { |
+ checkFile(''' |
+abstract class A { |
+ bool operator ==(Object object); |
+} |
+ |
+class B implements A {} |
+ |
+class F { |
+ void f(x) {} |
+ void g(int x) {} |
+} |
+ |
+class G extends F { |
+ /*severe:INVALID_METHOD_OVERRIDE*/void f(int x) {} |
+ void g(dynamic x) {} |
+} |
+ |
+class H implements F { |
+ /*severe:INVALID_METHOD_OVERRIDE*/void f(int x) {} |
+ void g(dynamic x) {} |
+} |
+'''); |
+ } |
+ |
+ void test_mixinOverrideOfGrandInterface_interfaceOfAbstractSuperclass() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class I1 { |
+ m(A a); |
+} |
+abstract class Base implements I1 {} |
+ |
+class M { |
+ m(B a) {} |
+} |
+ |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 extends Base |
+ with /*severe:INVALID_METHOD_OVERRIDE*/M {} |
+'''); |
+ } |
+ |
+ void test_mixinOverrideOfGrandInterface_interfaceOfConcreteSuperclass() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class I1 { |
+ m(A a); |
+} |
+ |
+class /*warning:NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE*/Base |
+ implements I1 {} |
+ |
+class M { |
+ m(B a) {} |
+} |
+ |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 extends Base |
+ with M {} |
+'''); |
+ } |
+ |
+ void test_mixinOverrideOfGrandInterface_interfaceOfInterfaceOfChild() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class I1 { |
+ m(A a); |
+} |
+abstract class I2 implements I1 {} |
+ |
+class M { |
+ m(B a) {} |
+} |
+ |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
+ extends Object with /*severe:INVALID_METHOD_OVERRIDE*/M |
+ implements I2 {} |
+'''); |
+ } |
+ |
+ void test_mixinOverrideOfGrandInterface_mixinOfInterfaceOfChild() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class M1 { |
+ m(A a); |
+} |
+abstract class I2 extends Object with M1 {} |
+ |
+class M { |
+ m(B a) {} |
+} |
+ |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
+ extends Object with /*severe:INVALID_METHOD_OVERRIDE*/M |
+ implements I2 {} |
+'''); |
+ } |
+ |
+ void test_mixinOverrideOfGrandInterface_superclassOfInterfaceOfChild() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class I1 { |
+ m(A a); |
+} |
+abstract class I2 extends I1 {} |
+ |
+class M { |
+ m(B a) {} |
+} |
+ |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
+ extends Object with /*severe:INVALID_METHOD_OVERRIDE*/M |
+ implements I2 {} |
+'''); |
+ } |
+ |
+ void |
+ test_noDuplicateReportsFromOverridingInterfaces_baseTypeAndMixinOverrideSameMethodInInterface() { |
+ checkFile(''' |
+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 /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
+ /*severe:INVALID_METHOD_OVERRIDE*/extends Base |
+ with /*severe:INVALID_METHOD_OVERRIDE*/M |
+ implements I1 {} |
+'''); |
+ } |
+ |
+ void |
+ test_noDuplicateReportsFromOverridingInterfaces_twoGrandTypesOverrideSameMethodInInterface() { |
+ checkFile(''' |
+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 /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
+ /*severe:INVALID_METHOD_OVERRIDE*/extends Parent1 |
+ implements I1 {} |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T2 |
+ /*severe:INVALID_METHOD_OVERRIDE*/extends Parent2 |
+ implements I1 {} |
+'''); |
+ } |
+ |
+ void |
+ test_noDuplicateReportsFromOverridingInterfaces_twoMixinsOverrideSameMethodInInterface() { |
+ checkFile(''' |
+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 /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 extends Object |
+ with /*severe:INVALID_METHOD_OVERRIDE*/M1, |
+ /*severe:INVALID_METHOD_OVERRIDE*/M2 |
+ implements I1 {} |
+'''); |
+ } |
+ |
+ void |
+ test_noDuplicateReportsFromOverridingInterfaces_typeAndBaseTypeOverrideSameMethodInInterface() { |
+ checkFile(''' |
+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:INVALID_METHOD_OVERRIDE*/m( |
+ /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
+} |
+ |
+// If there is no error in the class, we do report the error at |
+// the base class: |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T2 |
+ /*severe:INVALID_METHOD_OVERRIDE*/extends Base |
+ implements I1 {} |
+'''); |
+ } |
+ |
+ void |
+ test_noDuplicateReportsFromOverridingInterfaces_typeAndMixinOverrideSameMethodInInterface() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class I1 { |
+ m(A a); |
+} |
+ |
+class M { |
+ m(B a) {} |
+} |
+ |
+class T1 extends Object with M implements I1 { |
+ /*severe:INVALID_METHOD_OVERRIDE*/m( |
+ /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
+} |
+ |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T2 |
+ extends Object with /*severe:INVALID_METHOD_OVERRIDE*/M |
+ implements I1 {} |
+'''); |
+ } |
+ |
+ void |
+ test_noDuplicateReportsFromOverridingInterfaces_typeOverridesSomeMethodInMultipleInterfaces() { |
+ checkFile(''' |
+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:INVALID_METHOD_OVERRIDE*/m( |
+ /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
+} |
+'''); |
+ } |
+ |
+ void test_nullCoalescingOperator() { |
+ checkFile(''' |
+class A {} |
+class C<T> {} |
+main() { |
+ A a, b; |
+ a ??= new A(); |
+ b = b ?? new A(); |
+ |
+ // downwards inference |
+ C<int> c, d; |
+ c ??= /*info:INFERRED_TYPE_ALLOCATION*/new C(); |
+ d = d ?? /*info:INFERRED_TYPE_ALLOCATION*/new C(); |
+} |
+'''); |
+ } |
+ |
+ void test_privateOverride() { |
+ addFile( |
+ ''' |
+import 'main.dart' as main; |
+ |
+class Base { |
+ var f1; |
+ var _f2; |
+ var _f3; |
+ get _f4 => null; |
+ |
+ int _m1() => null; |
+} |
+ |
+class GrandChild extends main.Child { |
+ /*severe:INVALID_FIELD_OVERRIDE*/var _f2; |
+ /*severe:INVALID_FIELD_OVERRIDE*/var _f3; |
+ var _f4; |
+ |
+ /*severe:INVALID_METHOD_OVERRIDE*/String |
+ /*warning:INVALID_METHOD_OVERRIDE_RETURN_TYPE*/_m1() => null; |
+} |
+''', |
+ name: '/helper.dart'); |
+ checkFile(''' |
+import 'helper.dart' as helper; |
+ |
+class Child extends helper.Base { |
+ /*severe:INVALID_FIELD_OVERRIDE*/var f1; |
+ var _f2; |
+ var _f4; |
+ |
+ String _m1() => null; |
+} |
+'''); |
+ } |
+ |
+ void test_redirectingConstructor() { |
+ checkFile(''' |
+class A { |
+ A(A x) {} |
+ A.two() : this(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3); |
+} |
+'''); |
+ } |
+ |
+ void test_relaxedCasts() { |
+ checkFile(''' |
+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; |
+ lOfDs = new L(); // Reset type propagation. |
+ } |
+ { |
+ lOfOs = mOfDs; |
+ lOfOs = mOfOs; |
+ lOfOs = mOfAs; |
+ lOfOs = lOfDs; |
+ lOfOs = lOfOs; |
+ lOfOs = lOfAs; |
+ lOfOs = new L<Object>(); // Reset type propagation. |
+ } |
+ { |
+ lOfAs = /*warning:DOWN_CAST_COMPOSITE*/mOfDs; |
+ lOfAs = /*warning:INVALID_ASSIGNMENT*/mOfOs; |
+ lOfAs = mOfAs; |
+ lOfAs = /*warning:DOWN_CAST_COMPOSITE*/lOfDs; |
+ lOfAs = /*info:DOWN_CAST_IMPLICIT*/lOfOs; |
+ lOfAs = lOfAs; |
+ lOfAs = new L<A>(); // Reset type propagation. |
+ } |
+ { |
+ mOfDs = mOfDs; |
+ mOfDs = mOfOs; |
+ mOfDs = mOfAs; |
+ mOfDs = /*info:DOWN_CAST_IMPLICIT*/lOfDs; |
+ mOfDs = /*info:DOWN_CAST_IMPLICIT*/lOfOs; |
+ mOfDs = /*warning:DOWN_CAST_COMPOSITE*/lOfAs; |
+ mOfDs = new M(); // Reset type propagation. |
+ } |
+ { |
+ mOfOs = mOfDs; |
+ mOfOs = mOfOs; |
+ mOfOs = mOfAs; |
+ mOfOs = /*info:DOWN_CAST_IMPLICIT*/lOfDs; |
+ mOfOs = /*info:DOWN_CAST_IMPLICIT*/lOfOs; |
+ mOfOs = /*warning:INVALID_ASSIGNMENT*/lOfAs; |
+ mOfOs = new M<Object>(); // Reset type propagation. |
+ } |
+ { |
+ mOfAs = /*warning:DOWN_CAST_COMPOSITE*/mOfDs; |
+ mOfAs = /*info:DOWN_CAST_IMPLICIT*/mOfOs; |
+ mOfAs = mOfAs; |
+ mOfAs = /*warning:DOWN_CAST_COMPOSITE*/lOfDs; |
+ mOfAs = /*info:DOWN_CAST_IMPLICIT*/lOfOs; |
+ mOfAs = /*info:DOWN_CAST_IMPLICIT*/lOfAs; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_setterOverride_fuzzyArrows() { |
+ checkFile(''' |
+typedef void ToVoid<T>(T x); |
+class F { |
+ void set f(ToVoid<dynamic> x) {} |
+ void set g(ToVoid<int> x) {} |
+ void set h(dynamic x) {} |
+ void set i(int x) {} |
+} |
+ |
+class G extends F { |
+ /*severe:INVALID_METHOD_OVERRIDE*/void set f(ToVoid<int> x) {} |
+ void set g(ToVoid<dynamic> x) {} |
+ void set h(int x) {} |
+ /*severe:INVALID_METHOD_OVERRIDE*/void set i(dynamic x) {} |
+} |
+ |
+class H implements F { |
+ /*severe:INVALID_METHOD_OVERRIDE*/void set f(ToVoid<int> x) {} |
+ void set g(ToVoid<dynamic> x) {} |
+ void set h(int x) {} |
+ /*severe:INVALID_METHOD_OVERRIDE*/void set i(dynamic x) {} |
+} |
+ '''); |
+ } |
+ |
+ void test_setterReturnTypes() { |
+ checkFile(''' |
+void voidFn() => null; |
+class A { |
+ set a(y) => 4; |
+ set b(y) => voidFn(); |
+ void set c(y) => /*warning:RETURN_OF_INVALID_TYPE*/4; |
+ void set d(y) => voidFn(); |
+ /*warning:NON_VOID_RETURN_FOR_SETTER*/int set e(y) => 4; |
+ /*warning:NON_VOID_RETURN_FOR_SETTER*/int set f(y) => |
+ /*warning:RETURN_OF_INVALID_TYPE*/voidFn(); |
+ set g(y) {return /*warning:RETURN_OF_INVALID_TYPE*/4;} |
+ void set h(y) {return /*warning:RETURN_OF_INVALID_TYPE*/4;} |
+ /*warning:NON_VOID_RETURN_FOR_SETTER*/int set i(y) {return 4;} |
+} |
+'''); |
+ } |
+ |
+ void test_setterSetterOverride() { |
+ checkFile(''' |
+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:INVALID_METHOD_OVERRIDE*/void set f2(C value) {} |
+ void set f3(value) {} |
+ /*severe:INVALID_METHOD_OVERRIDE*/void set f4(dynamic value) {} |
+ set f5(B value) {} |
+} |
+'''); |
+ } |
+ |
+ void test_superCallPlacement() { |
+ checkFile(''' |
+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:INVALID_SUPER_INVOCATION*/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(); |
+'''); |
+ } |
+ |
+ void test_superclassOverrideOfGrandInterface_interfaceOfAbstractSuperclass() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class I1 { |
+ m(A a); |
+} |
+ |
+abstract class Base implements I1 { |
+ /*severe:INVALID_METHOD_OVERRIDE*/m( |
+ /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/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:INVALID_METHOD_OVERRIDE*/m(B a) {} |
+} |
+'''); |
+ } |
+ |
+ void test_superclassOverrideOfGrandInterface_interfaceOfConcreteSuperclass() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class I1 { |
+ m(A a); |
+} |
+ |
+class Base implements I1 { |
+ /*severe:INVALID_METHOD_OVERRIDE*/m( |
+ /*warning:INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE*/B a) {} |
+} |
+ |
+class T1 extends Base { |
+ m(B a) {} |
+} |
+'''); |
+ } |
+ |
+ void test_superclassOverrideOfGrandInterface_interfaceOfInterfaceOfChild() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class I1 { |
+ m(A a); |
+} |
+abstract class I2 implements I1 {} |
+ |
+class Base { |
+ m(B a) {} |
+} |
+ |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
+ /*severe:INVALID_METHOD_OVERRIDE*/extends Base implements I2 {} |
+'''); |
+ } |
+ |
+ void test_superclassOverrideOfGrandInterface_mixinOfInterfaceOfChild() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class M1 { |
+ m(A a); |
+} |
+abstract class I2 extends Object with M1 {} |
+ |
+class Base { |
+ m(B a) {} |
+} |
+ |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
+ /*severe:INVALID_METHOD_OVERRIDE*/extends Base |
+ implements I2 {} |
+'''); |
+ } |
+ |
+ void test_superclassOverrideOfGrandInterface_superclassOfInterfaceOfChild() { |
+ checkFile(''' |
+class A {} |
+class B {} |
+ |
+abstract class I1 { |
+ m(A a); |
+} |
+abstract class I2 extends I1 {} |
+ |
+class Base { |
+ m(B a) {} |
+} |
+ |
+class /*warning:INCONSISTENT_METHOD_INHERITANCE*/T1 |
+ /*severe:INVALID_METHOD_OVERRIDE*/extends Base |
+ implements I2 {} |
+'''); |
+ } |
+ |
+ void test_superConstructor() { |
+ checkFile(''' |
+class A { A(A x) {} } |
+class B extends A { |
+ B() : super(/*warning:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3); |
+} |
+'''); |
+ } |
+ |
+ void test_ternaryOperator() { |
+ checkFile(''' |
+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; |
+ |
+ // The warning on assigning to _comparator is legitimate. Since K has |
+ // no bound, all we know is that it's object. _comparator's function |
+ // type is effectively: (Object, Object) -> int |
+ // We are assigning it a fn of type: (Comparable, Comparable) -> int |
+ // There's no telling if that will work. For example, consider: |
+ // |
+ // new SplayTreeMap<Uri>(); |
+ // |
+ // This would end up calling .compareTo() on a Uri, which doesn't |
+ // define that since it doesn't implement Comparable. |
+ SplayTreeMap([int compare(K key1, K key2), |
+ bool isValidKey(potentialKey)]) |
+ : _comparator = /*warning:DOWN_CAST_COMPOSITE*/(compare == null) ? Comparable.compare : compare, |
+ _validKey = (isValidKey != null) ? isValidKey : ((v) => true) { |
+ _Predicate<Object> v = (isValidKey != null) |
+ ? isValidKey : (/*info:INFERRED_TYPE_CLOSURE*/(_) => true); |
+ |
+ v = (isValidKey != null) |
+ ? v : (/*info:INFERRED_TYPE_CLOSURE*/(_) => true); |
+ } |
+} |
+void main() { |
+ Object obj = 42; |
+ dynamic dyn = 42; |
+ int i = 42; |
+ |
+ // Check the boolean conversion of the condition. |
+ print(/*warning:NON_BOOL_CONDITION*/i ? false : true); |
+ print((/*info:DOWN_CAST_IMPLICIT*/obj) ? false : true); |
+ print((/*info:DYNAMIC_CAST*/dyn) ? false : true); |
+} |
+'''); |
+ } |
+ |
+ void test_typeCheckingLiterals() { |
+ checkFile(''' |
+test() { |
+ num n = 3; |
+ int i = 3; |
+ String s = "hello"; |
+ { |
+ List<int> l = <int>[i]; |
+ l = <int>[/*warning:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/s]; |
+ l = <int>[/*info:DOWN_CAST_IMPLICIT*/n]; |
+ l = <int>[i, /*info:DOWN_CAST_IMPLICIT*/n, /*warning:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/s]; |
+ } |
+ { |
+ List l = /*info:INFERRED_TYPE_LITERAL*/[i]; |
+ l = /*info:INFERRED_TYPE_LITERAL*/[s]; |
+ l = /*info:INFERRED_TYPE_LITERAL*/[n]; |
+ l = /*info:INFERRED_TYPE_LITERAL*/[i, n, s]; |
+ } |
+ { |
+ Map<String, int> m = <String, int>{s: i}; |
+ m = <String, int>{s: /*warning:MAP_VALUE_TYPE_NOT_ASSIGNABLE*/s}; |
+ m = <String, int>{s: /*info:DOWN_CAST_IMPLICIT*/n}; |
+ m = <String, int>{s: i, |
+ s: /*info:DOWN_CAST_IMPLICIT*/n, |
+ s: /*warning:MAP_VALUE_TYPE_NOT_ASSIGNABLE*/s}; |
+ } |
+ // TODO(leafp): We can't currently test for key errors since the |
+ // error marker binds to the entire entry. |
+ { |
+ Map m = /*info:INFERRED_TYPE_LITERAL*/{s: i}; |
+ m = /*info:INFERRED_TYPE_LITERAL*/{s: s}; |
+ m = /*info:INFERRED_TYPE_LITERAL*/{s: n}; |
+ m = /*info:INFERRED_TYPE_LITERAL*/ |
+ {s: i, |
+ s: n, |
+ s: s}; |
+ m = /*info:INFERRED_TYPE_LITERAL*/ |
+ {i: s, |
+ n: s, |
+ s: s}; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_typePromotionFromDynamic() { |
+ checkFile(r''' |
+f() { |
+ dynamic x; |
+ if (x is int) { |
+ int y = x; |
+ String z = /*warning:INVALID_ASSIGNMENT*/x; |
+ } |
+} |
+g() { |
+ Object x; |
+ if (x is int) { |
+ int y = x; |
+ String z = /*warning:INVALID_ASSIGNMENT*/x; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_typeSubtyping_assigningClass() { |
+ checkFile(''' |
+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 = /*warning:INVALID_ASSIGNMENT*/a; |
+ d = /*warning:INVALID_ASSIGNMENT*/a; |
+ n = /*warning:INVALID_ASSIGNMENT*/a; |
+ a = a; |
+ b = /*info:DOWN_CAST_IMPLICIT*/a; |
+} |
+'''); |
+ } |
+ |
+ void test_typeSubtyping_assigningSubclass() { |
+ checkFile(''' |
+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 = /*warning:INVALID_ASSIGNMENT*/b; |
+ d = /*warning:INVALID_ASSIGNMENT*/b; |
+ n = /*warning:INVALID_ASSIGNMENT*/b; |
+ a = b; |
+ b = b; |
+ c = /*warning:INVALID_ASSIGNMENT*/b; |
+} |
+'''); |
+ } |
+ |
+ void test_typeSubtyping_dynamicDowncasts() { |
+ checkFile(''' |
+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:DYNAMIC_CAST*/y; |
+ d = /*info:DYNAMIC_CAST*/y; |
+ n = /*info:DYNAMIC_CAST*/y; |
+ a = /*info:DYNAMIC_CAST*/y; |
+ b = /*info:DYNAMIC_CAST*/y; |
+} |
+'''); |
+ } |
+ |
+ void test_typeSubtyping_dynamicIsTop() { |
+ checkFile(''' |
+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; |
+} |
+'''); |
+ } |
+ |
+ void test_typeSubtyping_interfaces() { |
+ checkFile(''' |
+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:DOWN_CAST_IMPLICIT*/top; |
+ left = left; |
+ left = /*warning:INVALID_ASSIGNMENT*/right; |
+ left = bot; |
+ } |
+ { |
+ right = /*info:DOWN_CAST_IMPLICIT*/top; |
+ right = /*warning:INVALID_ASSIGNMENT*/left; |
+ right = right; |
+ right = bot; |
+ } |
+ { |
+ bot = /*info:DOWN_CAST_IMPLICIT*/top; |
+ bot = /*info:DOWN_CAST_IMPLICIT*/left; |
+ bot = /*info:DOWN_CAST_IMPLICIT*/right; |
+ bot = bot; |
+ } |
+} |
+'''); |
+ } |
+ |
+ void test_unaryOperators() { |
+ checkFile(''' |
+class A { |
+ A operator ~() => null; |
+ A operator +(int x) => null; |
+ A operator -(int x) => null; |
+ A operator -() => null; |
+} |
+ |
+foo() => new A(); |
+ |
+test() { |
+ A a = new A(); |
+ var c = foo(); |
+ dynamic d; |
+ |
+ ~a; |
+ (/*info:DYNAMIC_INVOKE*/~d); |
+ |
+ !/*warning:NON_BOOL_NEGATION_EXPRESSION*/a; |
+ !/*info:DYNAMIC_CAST*/d; |
+ |
+ -a; |
+ (/*info:DYNAMIC_INVOKE*/-d); |
+ |
+ ++a; |
+ --a; |
+ (/*info:DYNAMIC_INVOKE*/++d); |
+ (/*info:DYNAMIC_INVOKE*/--d); |
+ |
+ a++; |
+ a--; |
+ (/*info:DYNAMIC_INVOKE*/d++); |
+ (/*info:DYNAMIC_INVOKE*/d--); |
+}'''); |
+ } |
+ |
+ void test_unboundRedirectingConstructor() { |
+ // This is a regression test for https://github.com/dart-lang/sdk/issues/25071 |
+ checkFile(''' |
+class Foo { |
+ Foo() : /*severe:REDIRECT_GENERATIVE_TO_MISSING_CONSTRUCTOR*/this.init(); |
+} |
+ '''); |
+ } |
+ |
+ void test_unboundTypeName() { |
+ checkFile(''' |
+void main() { |
+ /*warning:UNDEFINED_CLASS should be error*/AToB y; |
+} |
+'''); |
+ } |
+ |
+ void test_unboundVariable() { |
+ checkFile(''' |
+void main() { |
+ dynamic y = /*warning:UNDEFINED_IDENTIFIER should be error*/unboundVariable; |
+} |
+'''); |
+ } |
+ |
+ void test_voidSubtyping() { |
+ // Regression test for https://github.com/dart-lang/sdk/issues/25069 |
+ checkFile(''' |
+typedef int Foo(); |
+void foo() {} |
+void main () { |
+ Foo x = /*warning:INVALID_ASSIGNMENT,info:USE_OF_VOID_RESULT*/foo(); |
+} |
+'''); |
+ } |
} |