| // Copyright (c) 2020, 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. |
| |
| // Test that inheriting types on overriding members work as specified. |
| |
| // ignore_for_file: unused_local_variable |
| |
| import "package:expect/expect.dart"; |
| |
| // Helper variables. |
| int intVar = 0; |
| List<int> listIntVar = <int>[]; |
| int? intQVar; |
| List<int?> listIntQVar = <int?>[]; |
| num numVar = 0; |
| List<num> listNumVar = <num>[]; |
| |
| /// Override can work with incompatible superinterfaces, |
| /// as long as override is more specific than all of them, |
| /// and it specifies all types. |
| class IIntOpts { |
| int foo(int x, {int v = 0, required int z}) => x; |
| } |
| |
| class IDoubleOpts { |
| double foo(double x, {double w = 0.0, int z = 1}) => x; |
| } |
| |
| class CNumOpts implements IIntOpts, IDoubleOpts { |
| // Override is more specific than all supertype members. |
| Never foo(num x, {int v = 0, double w = 0.0, int z = 1}) => throw "Never"; |
| } |
| |
| // When a type is omitted, the most specific immediate superinterface member |
| // signature is used. One such must exist. |
| |
| class IIntInt { |
| int foo(int x) => x; |
| } |
| |
| class INumInt { |
| int foo(num x) => x.toInt(); |
| } |
| |
| class IIntNum { |
| num foo(int x) => x.toInt(); |
| } |
| |
| class IInt { |
| void foo(int x) {} |
| } |
| |
| class IIntQ { |
| void foo(int? x) {} |
| } |
| |
| class IOpt1 { |
| void foo([int x = 0]) {} |
| } |
| |
| class IOpt2 { |
| void foo([int x = 0, int y = 0]) {} |
| } |
| |
| class IOptX { |
| void foo({int x = 0}) {} |
| } |
| |
| class IOptXY { |
| void foo({int x = 0, int y = 0}) {} |
| } |
| |
| abstract class IOptA1 { |
| void foo([int x]); |
| } |
| |
| abstract class IOptAX { |
| void foo({int x}); |
| } |
| |
| class IReq { |
| void foo({required int x}) {} |
| } |
| |
| // Type is inherited as long as no other type is written. |
| // Even if prefixed by `var`, `final`, `required` or `covariant`, |
| // or if made optional with or without a default value. |
| class CVar implements IIntInt { |
| foo(var x) { |
| // Checks that x is exactly int. |
| // It is assignable in both directions, and it's not dynamic. |
| intVar = x; |
| x = intVar; |
| var xList = [x]; |
| listIntVar = xList; |
| return intVar; |
| } |
| } |
| |
| class CFinal implements IInt { |
| foo(final x) { |
| var sameTypeAsX = x; |
| intVar = x; |
| sameTypeAsX = intVar; |
| var xList = [x]; |
| listIntVar = xList; |
| } |
| } |
| |
| class COptDefault implements IInt { |
| foo([x = 0]) { |
| intVar = x; |
| x = intVar; |
| var xList = [x]; |
| listIntVar = xList; |
| } |
| } |
| |
| // Must use the nullable type when not having a default. |
| class COptNoDefault implements IIntQ { |
| foo([x]) { |
| int? tmp = x; |
| x = tmp; |
| var xList = [x]; |
| listIntQVar = xList; |
| } |
| } |
| |
| class CReq implements IReq { |
| foo({required x}) { |
| intVar = x; |
| x = intVar; |
| var xList = [x]; |
| listIntQVar = xList; |
| } |
| } |
| |
| // Do inherit when specifying `covariant`. |
| class CCovar implements IInt { |
| foo(covariant x) { |
| intVar = x; |
| x = intVar; |
| var xList = [x]; |
| listIntVar = xList; |
| } |
| } |
| |
| class CCovar2 implements CCovar { |
| // Method was covariant in CCovar. |
| foo(Never x) => 0; |
| } |
| |
| /// A more specific `foo` than [IInt.foo]. |
| /// Subclass inherits types from most specific superclass member. |
| class CInherit1 implements INumInt, IIntNum { |
| foo(x) { |
| // x is num. |
| numVar = x; |
| x = numVar; |
| var xList = [x]; |
| listNumVar = xList; |
| |
| // return type is int. |
| var ret = foo(x); |
| intVar = ret; |
| ret = intVar; |
| var retList = [ret]; |
| listIntVar = retList; |
| return 0; |
| } |
| } |
| |
| class CInherit2 extends INumInt implements IIntNum { |
| foo(x) { |
| numVar = x; |
| x = numVar; |
| var xList = [x]; |
| listNumVar = xList; |
| |
| var ret = foo(x); |
| intVar = ret; |
| ret = intVar; |
| var retList = [ret]; |
| listIntVar = retList; |
| return 0; |
| } |
| } |
| |
| class CInherit3 extends IIntNum implements INumInt { |
| foo(x) { |
| numVar = x; |
| x = numVar; |
| var xList = [x]; |
| listNumVar = xList; |
| |
| var ret = foo(x); |
| intVar = ret; |
| ret = intVar; |
| var retList = [ret]; |
| listIntVar = retList; |
| return intVar; |
| } |
| } |
| |
| class CInherit4 with IIntNum implements INumInt { |
| foo(x) { |
| numVar = x; |
| x = numVar; |
| var xList = [x]; |
| listNumVar = xList; |
| |
| var ret = foo(x); |
| intVar = ret; |
| ret = intVar; |
| var retList = [ret]; |
| listIntVar = retList; |
| return intVar; |
| } |
| } |
| |
| void testInheritFull() { |
| Expect.type<int Function(num)>(CInherit1().foo); |
| Expect.type<int Function(num)>(CInherit2().foo); |
| Expect.type<int Function(num)>(CInherit3().foo); |
| Expect.type<int Function(num)>(CInherit4().foo); |
| } |
| |
| /// Works for optional parameters too. |
| |
| class COptInherit1 implements IOpt1, IOpt2 { |
| foo([x = 0, y = 0]) { |
| intVar = x; |
| x = intVar; |
| var listX = [x]; |
| listIntVar = listX; |
| |
| intVar = y; |
| y = intVar; |
| var listY = [y]; |
| listIntVar = listY; |
| } |
| } |
| |
| class COptInherit2 implements IOptX, IOptXY { |
| foo({x = 0, y = 0}) { |
| intVar = x; |
| x = intVar; |
| var listX = [x]; |
| listIntVar = listX; |
| |
| intVar = y; |
| y = intVar; |
| var listY = [y]; |
| listIntVar = listY; |
| } |
| } |
| |
| class COptInherit3 implements IIntInt, INumInt { |
| foo(x, [y]) { |
| // Ensure that type is: int Function(num, [dynamic]) . |
| // For static checks only, do not call the method! |
| |
| // x is exactly num. |
| numVar = x; |
| x = numVar; |
| var listX = [x]; |
| listNumVar = listX; |
| |
| // y is dynamic. |
| Object? tmpObject; |
| y = tmpObject; // A top type. |
| Null tmpNull = y; // Implicit downcast. |
| y.arglebargle(); // Unsound member invocations. |
| |
| // return type is exactly int. |
| var ret = foo(x, y); |
| intVar = ret; |
| ret = intVar; |
| var retList = [ret]; |
| listIntVar = retList; |
| return intVar; |
| } |
| } |
| |
| class COptInherit4 implements IOptA1 { |
| foo([x = 1]) { |
| intVar = x; |
| x = intVar; |
| var listX = [x]; |
| listIntVar = listX; |
| } |
| } |
| |
| class COptInherit5 implements IOptAX { |
| foo({x = 1}) { |
| intVar = x; |
| x = intVar; |
| var listX = [x]; |
| listIntVar = listX; |
| } |
| } |
| |
| void testInheritOpt() { |
| Expect.type<void Function([int, int])>(COptInherit1().foo); |
| Expect.type<void Function({int x, int y})>(COptInherit2().foo); |
| Expect.type<int Function(num, [dynamic])>(COptInherit3().foo); |
| Expect.type<void Function([int])>(COptInherit4().foo); |
| Expect.type<void Function({int x})>(COptInherit5().foo); |
| } |
| |
| // Do not inherit `final` with the type. |
| class IFinal { |
| void foo(final int x) {} |
| } |
| |
| class CInheritFinal implements IFinal { |
| void foo(x) { |
| x = 42; |
| x.toRadixString(16); |
| } |
| } |
| |
| // Also applies to getters and setters. |
| class IGetSetInt { |
| int get foo => 0; |
| set foo(int _) {} |
| } |
| |
| class IFieldInt { |
| int foo = 0; |
| } |
| |
| class ILateFieldInt { |
| late int foo; |
| } |
| |
| class IFinalFieldInt { |
| final int foo = 0; |
| } |
| |
| class CInheritGetSet implements IGetSetInt { |
| get foo => throw "whatever"; |
| set foo(set) { |
| // For static checking only, do not call. |
| // `set` is assignable both ways to int. |
| intVar = set; |
| set = intVar; |
| var listSet = [set]; |
| listIntVar = listSet; |
| |
| var get = foo; |
| // get is assignable both ways to int. |
| intVar = get; |
| get = intVar; |
| var listGet = [get]; |
| listIntVar = listGet; |
| } |
| } |
| |
| class CInheritField implements IFieldInt { |
| get foo => throw "whatever"; |
| set foo(set) { |
| // For static checking only, do not call. |
| // `set` is assignable both ways to int. |
| intVar = set; |
| set = intVar; |
| var listSet = [set]; |
| listIntVar = listSet; |
| |
| var get = foo; |
| // get is assignable both ways to int. |
| intVar = get; |
| get = intVar; |
| var listGet = [get]; |
| listIntVar = listGet; |
| } |
| } |
| |
| class CInheritLateField implements ILateFieldInt { |
| get foo => throw "whatever"; |
| set foo(set) { |
| // For static checking only, do not call. |
| // `set` is assignable both ways to int. |
| intVar = set; |
| set = intVar; |
| var listSet = [set]; |
| listIntVar = listSet; |
| |
| var get = foo; |
| // get is assignable both ways to int. |
| intVar = get; |
| get = intVar; |
| var listGet = [get]; |
| listIntVar = listGet; |
| } |
| } |
| |
| class CInheritFinalField implements IFinalFieldInt { |
| get foo => throw "whatever"; |
| set foo(set) { |
| // For static checking only, do not call. |
| |
| // `set` is assignable both ways to int. |
| intVar = set; |
| set = intVar; |
| var listSet = [set]; |
| listIntVar = listSet; |
| |
| var get = foo; // Is int. |
| // get is assignable both ways to int. |
| intVar = get; |
| get = intVar; |
| var listGet = [get]; |
| listIntVar = listGet; |
| } |
| } |
| |
| class ISetterOnly { |
| set foo(int value) {} |
| } |
| |
| class IInheritSetter implements ISetterOnly { |
| // Infers `int` as return type. |
| get foo => throw "whatever"; |
| |
| set foo(value) { |
| int tmp = value; |
| value = tmp; |
| var valueList = [value]; |
| List<int> list = valueList; |
| |
| var get = foo; |
| intVar = get; |
| get = intVar; |
| var getList = [get]; |
| list = getList; |
| } |
| } |
| |
| void main() { |
| testInheritFull(); |
| testInheritOpt(); |
| } |