| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:async'; |
| import "package:async_helper/async_helper.dart"; |
| import 'compiler_helper.dart'; |
| import "package:expect/expect.dart"; |
| |
| Future compile(String source) { |
| Uri uri = Uri.parse('test:code'); |
| var compiler = |
| compilerFor(source, uri, analyzeOnly: true, enableTypeAssertions: true); |
| compiler.diagnosticHandler = createHandler(compiler, source); |
| return compiler.run(uri).then((_) { |
| return compiler; |
| }); |
| } |
| |
| Future test(String source, {var errors, var warnings}) async { |
| if (errors == null) errors = []; |
| if (errors is! List) errors = [errors]; |
| if (warnings == null) warnings = []; |
| if (warnings is! List) warnings = [warnings]; |
| var compiler = await compile(source); |
| DiagnosticCollector collector = compiler.diagnosticCollector; |
| Expect.equals(!errors.isEmpty, compiler.compilationFailed); |
| Expect.equals( |
| errors.length, |
| collector.errors.length, |
| 'unexpected error count: ${collector.errors.length} ' |
| 'expected ${errors.length}'); |
| Expect.equals( |
| warnings.length, |
| collector.warnings.length, |
| 'unexpected warning count: ${collector.warnings.length} ' |
| 'expected ${warnings.length}'); |
| |
| for (int i = 0; i < errors.length; i++) { |
| Expect.equals(errors[i], collector.errors.elementAt(i).message.kind); |
| } |
| for (int i = 0; i < warnings.length; i++) { |
| Expect.equals(warnings[i], collector.warnings.elementAt(i).message.kind); |
| } |
| } |
| |
| Future test1() async { |
| var compiler = await compile(r""" |
| class A<T extends T> {} |
| |
| void main() { |
| new A(); |
| } |
| """); |
| DiagnosticCollector collector = compiler.diagnosticCollector; |
| Expect.isFalse(compiler.compilationFailed); |
| Expect.isTrue( |
| collector.errors.isEmpty, 'unexpected errors: ${collector.errors}'); |
| Expect.equals(1, collector.warnings.length, |
| 'expected exactly one warning, but got ${collector.warnings}'); |
| |
| print(collector.warnings.elementAt(0)); |
| Expect.equals(MessageKind.CYCLIC_TYPE_VARIABLE, |
| collector.warnings.elementAt(0).message.kind); |
| Expect.equals("T", |
| collector.warnings.elementAt(0).message.arguments['typeVariableName']); |
| } |
| |
| Future test2() async { |
| var compiler = await compile(r""" |
| class B<T extends S, S extends T> {} |
| |
| void main() { |
| new B(); |
| } |
| """); |
| DiagnosticCollector collector = compiler.diagnosticCollector; |
| Expect.isFalse(compiler.compilationFailed); |
| print(collector.errors); |
| Expect.isTrue(collector.errors.isEmpty, 'unexpected errors'); |
| Expect.equals(2, collector.warnings.length, |
| 'expected exactly two errors, but got ${collector.warnings}'); |
| |
| Expect.equals(MessageKind.CYCLIC_TYPE_VARIABLE, |
| collector.warnings.elementAt(0).message.kind); |
| Expect.equals("T", |
| collector.warnings.elementAt(0).message.arguments['typeVariableName']); |
| |
| Expect.equals(MessageKind.CYCLIC_TYPE_VARIABLE, |
| collector.warnings.elementAt(1).message.kind); |
| Expect.equals("S", |
| collector.warnings.elementAt(1).message.arguments['typeVariableName']); |
| } |
| |
| Future test3() async { |
| var compiler = await compile(r""" |
| class C<T extends S, S extends U, U extends T> {} |
| |
| void main() { |
| new C(); |
| } |
| """); |
| DiagnosticCollector collector = compiler.diagnosticCollector; |
| Expect.isFalse(compiler.compilationFailed); |
| print(collector.errors); |
| Expect.isTrue(collector.errors.isEmpty, 'unexpected errors'); |
| Expect.equals(3, collector.warnings.length, |
| 'expected exactly one error, but got ${collector.warnings}'); |
| |
| Expect.equals(MessageKind.CYCLIC_TYPE_VARIABLE, |
| collector.warnings.elementAt(0).message.kind); |
| Expect.equals("T", |
| collector.warnings.elementAt(0).message.arguments['typeVariableName']); |
| |
| Expect.equals(MessageKind.CYCLIC_TYPE_VARIABLE, |
| collector.warnings.elementAt(1).message.kind); |
| Expect.equals("S", |
| collector.warnings.elementAt(1).message.arguments['typeVariableName']); |
| |
| Expect.equals(MessageKind.CYCLIC_TYPE_VARIABLE, |
| collector.warnings.elementAt(2).message.kind); |
| Expect.equals("U", |
| collector.warnings.elementAt(2).message.arguments['typeVariableName']); |
| } |
| |
| Future test4() async { |
| var compiler = await compile(r""" |
| class D<T extends S, S extends U, U extends S> {} |
| |
| void main() { |
| new D(); |
| } |
| """); |
| DiagnosticCollector collector = compiler.diagnosticCollector; |
| Expect.isFalse(compiler.compilationFailed); |
| print(collector.errors); |
| Expect.isTrue(collector.errors.isEmpty, 'unexpected errors'); |
| Expect.equals(2, collector.warnings.length, |
| 'expected exactly one error, but got ${collector.warnings}'); |
| |
| Expect.equals(MessageKind.CYCLIC_TYPE_VARIABLE, |
| collector.warnings.elementAt(0).message.kind); |
| Expect.equals("S", |
| collector.warnings.elementAt(0).message.arguments['typeVariableName']); |
| |
| Expect.equals(MessageKind.CYCLIC_TYPE_VARIABLE, |
| collector.warnings.elementAt(1).message.kind); |
| Expect.equals("U", |
| collector.warnings.elementAt(1).message.arguments['typeVariableName']); |
| } |
| |
| Future test5() { |
| return test(r""" |
| class A<T extends num> {} |
| |
| void main() { |
| new A(); |
| new A<num>(); |
| new A<dynamic>(); |
| new A<int>(); |
| new A<double>(); |
| } |
| """); |
| } |
| |
| Future test6() { |
| return test( |
| r""" |
| class A<T extends num> {} |
| |
| void main() { |
| new A<String>(); |
| } |
| """, |
| warnings: MessageKind.INVALID_TYPE_VARIABLE_BOUND); |
| } |
| |
| Future test7() { |
| return test( |
| r""" |
| class A<T extends num> {} |
| class B<T> extends A<T> {} // Warning produced here. |
| |
| void main() { |
| new B(); // No warning produced here. |
| new B<String>(); // No warning produced here. |
| } |
| """, |
| warnings: MessageKind.INVALID_TYPE_VARIABLE_BOUND); |
| } |
| |
| Future test8() { |
| return test(r""" |
| class B<T extends B<T>> {} |
| class C<T extends B<T>> extends B<T> {} |
| class D<T extends C<T>> extends C<T> {} |
| class E<T extends E<T>> extends D<T> {} |
| class F extends E<F> {} |
| |
| void main() { |
| new B(); |
| new B<dynamic>(); |
| new B<F>(); |
| new B<B<F>>(); |
| new C(); |
| new C<dynamic>(); |
| new C<B<F>>(); |
| new D(); |
| new D<dynamic>(); |
| new D<C<F>>(); |
| new E(); |
| new E<dynamic>(); |
| new E<E<F>>(); |
| new F(); |
| } |
| """); |
| } |
| |
| Future test9() { |
| return test( |
| r""" |
| class B<T extends B<T>> {} |
| class C<T extends B<T>> extends B<T> {} |
| class D<T extends C<T>> extends C<T> {} |
| class E<T extends E<T>> extends D<T> {} |
| class F extends E<F> {} |
| |
| void main() { |
| new D<B<F>>(); // Warning: B<F> is not a subtype of C<T>. |
| new E<D<F>>(); // Warning: E<F> is not a subtype of E<T>. |
| } |
| """, |
| warnings: [ |
| MessageKind.INVALID_TYPE_VARIABLE_BOUND, |
| MessageKind.INVALID_TYPE_VARIABLE_BOUND |
| ]); |
| } |
| |
| Future test10() { |
| return test(r""" |
| class A { |
| const A(); |
| } |
| class Test<T extends A> { |
| final T x = const A(); |
| const Test(); |
| } |
| main() { |
| print(const Test<A>()); |
| } |
| """); |
| } |
| |
| // TODO(het): The error is reported twice because both the Dart and JS constant |
| // compilers are run on the const constructor, investigate why. |
| Future test11() { |
| return test( |
| r""" |
| class A { |
| const A(); |
| } |
| class B extends A { |
| const B(); |
| } |
| class Test<T extends A> { |
| final T x = const A(); |
| const Test(); |
| } |
| main() { |
| print(const Test<B>()); |
| } |
| """, |
| errors: [MessageKind.NOT_ASSIGNABLE, MessageKind.NOT_ASSIGNABLE]); |
| } |
| |
| main() { |
| asyncTest(() async { |
| await test1(); |
| await test2(); |
| await test3(); |
| await test4(); |
| await test5(); |
| await test6(); |
| await test7(); |
| await test8(); |
| await test9(); |
| await test10(); |
| await test11(); |
| }); |
| } |