| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| // Tests related to the [TypeSystem] class. |
| |
| import 'package:analyzer/dart/analysis/features.dart'; |
| import 'package:analyzer/dart/ast/standard_ast_factory.dart' show astFactory; |
| import 'package:analyzer/dart/ast/token.dart' show Keyword; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/nullability_suffix.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/error/listener.dart'; |
| import 'package:analyzer/src/dart/ast/token.dart' show KeywordToken; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| import 'package:analyzer/src/error/codes.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| import 'package:analyzer/src/generated/source.dart' |
| show NonExistingSource, UriKind; |
| import 'package:analyzer/src/generated/testing/element_factory.dart'; |
| import 'package:analyzer/src/generated/utilities_dart.dart'; |
| import 'package:meta/meta.dart'; |
| import 'package:path/path.dart' show toUri; |
| import 'package:test/test.dart'; |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| |
| import 'test_analysis_context.dart'; |
| |
| main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(AssignabilityTest); |
| defineReflectiveTests(ConstraintMatchingTest); |
| defineReflectiveTests(GenericFunctionInferenceTest); |
| defineReflectiveTests(GreatestLowerBoundTest); |
| defineReflectiveTests(LeastUpperBoundFunctionsTest); |
| defineReflectiveTests(LeastUpperBoundTest); |
| defineReflectiveTests(NonNullableSubtypingTest); |
| defineReflectiveTests(SubtypingTest); |
| defineReflectiveTests(TypeSystemTest); |
| }); |
| } |
| |
| abstract class AbstractTypeSystemTest { |
| TypeProvider typeProvider; |
| Dart2TypeSystem typeSystem; |
| |
| DartType get bottomType => typeProvider.bottomType; |
| |
| InterfaceType get doubleType => typeProvider.doubleType; |
| |
| DartType get dynamicType => typeProvider.dynamicType; |
| |
| InterfaceType get functionType => typeProvider.functionType; |
| |
| InterfaceType get intType => typeProvider.intType; |
| |
| DartType get neverType => typeProvider.neverType; |
| |
| DartType get nullType => typeProvider.nullType; |
| |
| InterfaceType get numType => typeProvider.numType; |
| |
| InterfaceType get objectType => typeProvider.objectType; |
| |
| InterfaceType get stringType => typeProvider.stringType; |
| |
| FeatureSet get testFeatureSet { |
| return FeatureSet.forTesting(); |
| } |
| |
| DartType get voidType => VoidTypeImpl.instance; |
| |
| DartType futureOrType(DartType T) { |
| var futureOrElement = typeProvider.futureOrElement; |
| return _interfaceType(futureOrElement, typeArguments: [T]); |
| } |
| |
| DartType futureType(DartType T) { |
| var futureElement = typeProvider.futureElement; |
| return _interfaceType(futureElement, typeArguments: [T]); |
| } |
| |
| DartType iterableType(DartType T) { |
| var iterableElement = typeProvider.iterableElement; |
| return _interfaceType(iterableElement, typeArguments: [T]); |
| } |
| |
| DartType listType(DartType T) { |
| var listElement = typeProvider.listElement; |
| return _interfaceType(listElement, typeArguments: [T]); |
| } |
| |
| void setUp() { |
| var analysisContext = TestAnalysisContext( |
| featureSet: testFeatureSet, |
| ); |
| typeProvider = analysisContext.typeProvider; |
| typeSystem = analysisContext.typeSystem; |
| } |
| |
| ClassElementImpl _class({ |
| @required String name, |
| bool isAbstract = false, |
| InterfaceType superType, |
| List<TypeParameterElement> typeParameters = const [], |
| List<InterfaceType> interfaces = const [], |
| List<InterfaceType> mixins = const [], |
| List<MethodElement> methods = const [], |
| }) { |
| var element = ClassElementImpl(name, 0); |
| element.typeParameters = typeParameters; |
| element.supertype = superType ?? objectType; |
| element.interfaces = interfaces; |
| element.mixins = mixins; |
| element.methods = methods; |
| return element; |
| } |
| |
| /** |
| * Creates a function type with the given parameter and return types. |
| * |
| * The return type defaults to `void` if omitted. |
| */ |
| FunctionType _functionType({ |
| List<TypeParameterElement> typeFormals, |
| List<DartType> required, |
| List<DartType> optional, |
| Map<String, DartType> named, |
| DartType returns, |
| NullabilitySuffix nullabilitySuffix = NullabilitySuffix.star, |
| }) { |
| if (optional != null && named != null) { |
| throw ArgumentError( |
| 'Cannot have both optional positional and named parameters.', |
| ); |
| } |
| |
| var parameters = <ParameterElement>[]; |
| if (required != null) { |
| for (var i = 0; i < required.length; ++i) { |
| parameters.add( |
| ParameterElementImpl.synthetic( |
| 'r$i', |
| required[i], |
| ParameterKind.REQUIRED, |
| ), |
| ); |
| } |
| } |
| if (optional != null) { |
| for (var i = 0; i < optional.length; ++i) { |
| parameters.add( |
| ParameterElementImpl.synthetic( |
| 'p$i', |
| optional[i], |
| ParameterKind.POSITIONAL, |
| ), |
| ); |
| } |
| } |
| if (named != null) { |
| for (var namedEntry in named.entries) { |
| parameters.add( |
| ParameterElementImpl.synthetic( |
| namedEntry.key, |
| namedEntry.value, |
| ParameterKind.NAMED, |
| ), |
| ); |
| } |
| } |
| |
| return FunctionTypeImpl.synthetic( |
| returns ?? voidType, |
| typeFormals ?? const <TypeParameterElement>[], |
| parameters, |
| nullabilitySuffix: nullabilitySuffix, |
| ); |
| } |
| |
| InterfaceType _interfaceType( |
| ClassElement element, { |
| List<DartType> typeArguments = const [], |
| NullabilitySuffix nullabilitySuffix = NullabilitySuffix.star, |
| }) { |
| return InterfaceTypeImpl.explicit( |
| element, |
| typeArguments, |
| nullabilitySuffix: nullabilitySuffix, |
| ); |
| } |
| |
| MethodElementImpl _method( |
| String name, |
| DartType returnType, { |
| List<TypeParameterElement> typeFormals = const [], |
| List<ParameterElement> parameters = const [], |
| }) { |
| var element = MethodElementImpl(name, 0) |
| ..parameters = parameters |
| ..returnType = returnType |
| ..typeParameters = typeFormals; |
| element.type = _typeOfExecutableElement(element); |
| return element; |
| } |
| |
| ParameterElement _requiredParameter(String name, DartType type) { |
| var parameter = ParameterElementImpl(name, 0); |
| parameter.parameterKind = ParameterKind.REQUIRED; |
| parameter.type = type; |
| return parameter; |
| } |
| |
| /// TODO(scheglov) We should do the opposite - build type in the element. |
| /// But build a similar synthetic / structured type. |
| FunctionType _typeOfExecutableElement(ExecutableElement element) { |
| return FunctionTypeImpl.synthetic( |
| element.returnType, |
| element.typeParameters, |
| element.parameters, |
| ); |
| } |
| |
| TypeParameterElementImpl _typeParameter(String name, {DartType bound}) { |
| var element = TypeParameterElementImpl.synthetic(name); |
| element.bound = bound; |
| return element; |
| } |
| |
| TypeParameterTypeImpl _typeParameterType( |
| TypeParameterElement element, { |
| NullabilitySuffix nullabilitySuffix = NullabilitySuffix.star, |
| }) { |
| return TypeParameterTypeImpl( |
| element, |
| nullabilitySuffix: nullabilitySuffix, |
| ); |
| } |
| } |
| |
| @reflectiveTest |
| class AssignabilityTest extends AbstractTypeSystemTest { |
| void test_isAssignableTo_bottom_isBottom() { |
| var A = _class(name: 'A'); |
| List<DartType> interassignable = <DartType>[ |
| dynamicType, |
| objectType, |
| intType, |
| doubleType, |
| numType, |
| stringType, |
| _interfaceType(A), |
| bottomType, |
| ]; |
| |
| _checkGroups(bottomType, interassignable: interassignable); |
| } |
| |
| void test_isAssignableTo_call_method() { |
| var B = _class( |
| name: 'B', |
| methods: [ |
| _method('call', objectType, parameters: [ |
| _requiredParameter('_', intType), |
| ]), |
| ], |
| ); |
| |
| _checkIsStrictAssignableTo( |
| _interfaceType(B), |
| _functionType(required: [intType], returns: objectType), |
| ); |
| } |
| |
| void test_isAssignableTo_classes() { |
| var classTop = _class(name: 'A'); |
| var classLeft = _class(name: 'B', superType: _interfaceType(classTop)); |
| var classRight = _class(name: 'C', superType: _interfaceType(classTop)); |
| var classBottom = _class( |
| name: 'D', |
| superType: _interfaceType(classLeft), |
| interfaces: [_interfaceType(classRight)], |
| ); |
| var top = _interfaceType(classTop); |
| var left = _interfaceType(classLeft); |
| var right = _interfaceType(classRight); |
| var bottom = _interfaceType(classBottom); |
| |
| _checkLattice(top, left, right, bottom); |
| } |
| |
| void test_isAssignableTo_double() { |
| var A = _class(name: 'A'); |
| List<DartType> interassignable = <DartType>[ |
| dynamicType, |
| objectType, |
| doubleType, |
| numType, |
| bottomType, |
| ]; |
| List<DartType> unrelated = <DartType>[ |
| intType, |
| stringType, |
| _interfaceType(A), |
| ]; |
| |
| _checkGroups(doubleType, |
| interassignable: interassignable, unrelated: unrelated); |
| } |
| |
| void test_isAssignableTo_dynamic_isTop() { |
| var A = _class(name: 'A'); |
| List<DartType> interassignable = <DartType>[ |
| dynamicType, |
| objectType, |
| intType, |
| doubleType, |
| numType, |
| stringType, |
| _interfaceType(A), |
| bottomType, |
| ]; |
| _checkGroups(dynamicType, interassignable: interassignable); |
| } |
| |
| void test_isAssignableTo_generics() { |
| var LT = _typeParameter('T'); |
| var L = _class(name: 'L', typeParameters: [LT]); |
| |
| var MT = _typeParameter('T'); |
| var M = _class( |
| name: 'M', |
| typeParameters: [MT], |
| interfaces: [ |
| _interfaceType( |
| L, |
| typeArguments: [_typeParameterType(MT)], |
| ), |
| ], |
| ); |
| |
| var top = _interfaceType(L, typeArguments: [dynamicType]); |
| var left = _interfaceType(M, typeArguments: [dynamicType]); |
| var right = _interfaceType(L, typeArguments: [intType]); |
| var bottom = _interfaceType(M, typeArguments: [intType]); |
| |
| _checkCrossLattice(top, left, right, bottom); |
| } |
| |
| void test_isAssignableTo_int() { |
| var A = _class(name: 'A'); |
| List<DartType> interassignable = <DartType>[ |
| dynamicType, |
| objectType, |
| intType, |
| numType, |
| bottomType, |
| ]; |
| List<DartType> unrelated = <DartType>[ |
| doubleType, |
| stringType, |
| _interfaceType(A), |
| ]; |
| |
| _checkGroups(intType, |
| interassignable: interassignable, unrelated: unrelated); |
| } |
| |
| void test_isAssignableTo_named_optional() { |
| var r = _functionType(required: [intType], returns: intType); |
| var o = _functionType(optional: [intType], returns: intType); |
| var n = _functionType(named: {'x': intType}, returns: intType); |
| |
| var rr = _functionType( |
| required: [intType, intType], |
| returns: intType, |
| ); |
| var ro = _functionType( |
| required: [intType], |
| optional: [intType], |
| returns: intType, |
| ); |
| var rn = _functionType( |
| required: [intType], |
| named: {'x': intType}, |
| returns: intType, |
| ); |
| var oo = _functionType( |
| optional: [intType, intType], |
| returns: intType, |
| ); |
| var nn = _functionType( |
| named: {'x': intType, 'y': intType}, |
| returns: intType, |
| ); |
| var nnn = _functionType( |
| named: {'x': intType, 'y': intType, 'z': intType}, |
| returns: intType, |
| ); |
| |
| _checkGroups(r, |
| interassignable: [r, o, ro, rn, oo], unrelated: [n, rr, nn, nnn]); |
| _checkGroups(o, |
| interassignable: [o, oo], unrelated: [n, rr, ro, rn, nn, nnn]); |
| _checkGroups(n, |
| interassignable: [n, nn, nnn], unrelated: [r, o, rr, ro, rn, oo]); |
| _checkGroups(rr, |
| interassignable: [rr, ro, oo], unrelated: [r, o, n, rn, nn, nnn]); |
| _checkGroups(ro, interassignable: [ro, oo], unrelated: [o, n, rn, nn, nnn]); |
| _checkGroups(rn, |
| interassignable: [rn], unrelated: [o, n, rr, ro, oo, nn, nnn]); |
| _checkGroups(oo, interassignable: [oo], unrelated: [n, rn, nn, nnn]); |
| _checkGroups(nn, |
| interassignable: [nn, nnn], unrelated: [r, o, rr, ro, rn, oo]); |
| _checkGroups(nnn, |
| interassignable: [nnn], unrelated: [r, o, rr, ro, rn, oo]); |
| } |
| |
| void test_isAssignableTo_num() { |
| var A = _class(name: 'A'); |
| List<DartType> interassignable = <DartType>[ |
| dynamicType, |
| objectType, |
| numType, |
| intType, |
| doubleType, |
| bottomType, |
| ]; |
| List<DartType> unrelated = <DartType>[ |
| stringType, |
| _interfaceType(A), |
| ]; |
| |
| _checkGroups(numType, |
| interassignable: interassignable, unrelated: unrelated); |
| } |
| |
| void test_isAssignableTo_simple_function() { |
| var top = _functionType(required: [intType], returns: objectType); |
| var left = _functionType(required: [intType], returns: intType); |
| var right = _functionType(required: [objectType], returns: objectType); |
| var bottom = _functionType(required: [objectType], returns: intType); |
| |
| _checkCrossLattice(top, left, right, bottom); |
| } |
| |
| void test_isAssignableTo_void_functions() { |
| var top = _functionType(required: [intType], returns: voidType); |
| var bottom = _functionType(required: [objectType], returns: intType); |
| |
| _checkEquivalent(bottom, top); |
| } |
| |
| void _checkCrossLattice( |
| DartType top, DartType left, DartType right, DartType bottom) { |
| _checkGroups(top, interassignable: [top, left, right, bottom]); |
| _checkGroups(left, |
| interassignable: [top, left, bottom], unrelated: [right]); |
| _checkGroups(right, |
| interassignable: [top, right, bottom], unrelated: [left]); |
| _checkGroups(bottom, interassignable: [top, left, right, bottom]); |
| } |
| |
| void _checkEquivalent(DartType type1, DartType type2) { |
| _checkIsAssignableTo(type1, type2); |
| _checkIsAssignableTo(type2, type1); |
| } |
| |
| void _checkGroups(DartType t1, |
| {List<DartType> interassignable, List<DartType> unrelated}) { |
| if (interassignable != null) { |
| for (DartType t2 in interassignable) { |
| _checkEquivalent(t1, t2); |
| } |
| } |
| if (unrelated != null) { |
| for (DartType t2 in unrelated) { |
| _checkUnrelated(t1, t2); |
| } |
| } |
| } |
| |
| void _checkIsAssignableTo(DartType type1, DartType type2) { |
| expect(typeSystem.isAssignableTo(type1, type2), true); |
| } |
| |
| void _checkIsNotAssignableTo(DartType type1, DartType type2) { |
| expect(typeSystem.isAssignableTo(type1, type2), false); |
| } |
| |
| void _checkIsStrictAssignableTo(DartType type1, DartType type2) { |
| _checkIsAssignableTo(type1, type2); |
| _checkIsNotAssignableTo(type2, type1); |
| } |
| |
| void _checkLattice( |
| DartType top, DartType left, DartType right, DartType bottom) { |
| _checkGroups(top, interassignable: <DartType>[top, left, right, bottom]); |
| _checkGroups(left, |
| interassignable: <DartType>[top, left, bottom], |
| unrelated: <DartType>[right]); |
| _checkGroups(right, |
| interassignable: <DartType>[top, right, bottom], |
| unrelated: <DartType>[left]); |
| _checkGroups(bottom, interassignable: <DartType>[top, left, right, bottom]); |
| } |
| |
| void _checkUnrelated(DartType type1, DartType type2) { |
| _checkIsNotAssignableTo(type1, type2); |
| _checkIsNotAssignableTo(type2, type1); |
| } |
| } |
| |
| /** |
| * Base class for testing LUB and GLB in spec and strong mode. |
| */ |
| abstract class BoundTestBase extends AbstractTypeSystemTest { |
| void _checkGreatestLowerBound( |
| DartType type1, DartType type2, DartType expectedResult) { |
| var glb = typeSystem.getGreatestLowerBound(type1, type2); |
| expect(glb, expectedResult); |
| // Check that the result is a lower bound. |
| expect(typeSystem.isSubtypeOf(glb, type1), true); |
| expect(typeSystem.isSubtypeOf(glb, type2), true); |
| // Check for symmetry while we're at it. Unfortunately, |
| // for function types, the current version of equality |
| // does not respect re-ordering of named parameters, so |
| // for function types we just check if they are mutual subtypes. |
| // https://github.com/dart-lang/sdk/issues/26126 |
| // TODO(leafp): Fix this. |
| glb = typeSystem.getGreatestLowerBound(type2, type1); |
| if (glb is FunctionTypeImpl) { |
| expect(typeSystem.isSubtypeOf(glb, expectedResult), true); |
| expect(typeSystem.isSubtypeOf(expectedResult, glb), true); |
| } else { |
| expect(glb, expectedResult); |
| } |
| } |
| |
| void _checkLeastUpperBound( |
| DartType type1, DartType type2, DartType expectedResult) { |
| var lub = typeSystem.getLeastUpperBound(type1, type2); |
| expect(lub, expectedResult); |
| // Check that the result is an upper bound. |
| expect(typeSystem.isSubtypeOf(type1, lub), true); |
| expect(typeSystem.isSubtypeOf(type2, lub), true); |
| |
| // Check for symmetry while we're at it. Unfortunately, |
| // for function types, the current version of equality |
| // does not respect re-ordering of named parameters, so |
| // for function types we just check if they are mutual subtypes. |
| // https://github.com/dart-lang/sdk/issues/26126 |
| // TODO(leafp): Fix this. |
| lub = typeSystem.getLeastUpperBound(type2, type1); |
| if (lub is FunctionTypeImpl) { |
| expect(typeSystem.isSubtypeOf(lub, expectedResult), true); |
| expect(typeSystem.isSubtypeOf(expectedResult, lub), true); |
| } else { |
| expect(lub, expectedResult); |
| } |
| } |
| } |
| |
| @reflectiveTest |
| class ConstraintMatchingTest extends AbstractTypeSystemTest { |
| TypeParameterType T; |
| |
| void setUp() { |
| super.setUp(); |
| T = _typeParameterType( |
| _typeParameter('T'), |
| nullabilitySuffix: NullabilitySuffix.star, |
| ); |
| } |
| |
| void test_function_coreFunction() { |
| _checkOrdinarySubtypeMatch( |
| _functionType(required: [intType], returns: stringType), |
| functionType, |
| [T], |
| covariant: true, |
| ); |
| } |
| |
| void test_function_parameter_types() { |
| _checkIsSubtypeMatchOf( |
| _functionType(required: [T], returns: intType), |
| _functionType(required: [stringType], returns: intType), |
| [T], |
| ['String <: T'], |
| covariant: true, |
| ); |
| } |
| |
| void test_function_return_types() { |
| _checkIsSubtypeMatchOf( |
| _functionType(required: [intType], returns: T), |
| _functionType(required: [intType], returns: stringType), |
| [T], |
| ['T <: String'], |
| covariant: true, |
| ); |
| } |
| |
| void test_futureOr_futureOr() { |
| _checkIsSubtypeMatchOf( |
| futureOrType(T), futureOrType(stringType), [T], ['T <: String'], |
| covariant: true); |
| } |
| |
| void test_futureOr_x_fail_future_branch() { |
| // FutureOr<List<T>> <: List<String> can't be satisfied because |
| // Future<List<T>> <: List<String> can't be satisfied |
| _checkIsNotSubtypeMatchOf( |
| futureOrType(listType(T)), listType(stringType), [T], |
| covariant: true); |
| } |
| |
| void test_futureOr_x_fail_nonFuture_branch() { |
| // FutureOr<List<T>> <: Future<List<String>> can't be satisfied because |
| // List<T> <: Future<List<String>> can't be satisfied |
| _checkIsNotSubtypeMatchOf( |
| futureOrType(listType(T)), futureType(listType(stringType)), [T], |
| covariant: true); |
| } |
| |
| void test_futureOr_x_success() { |
| // FutureOr<T> <: Future<T> can be satisfied by T=Null. At this point in |
| // the type inference algorithm all we figure out is that T must be a |
| // subtype of both String and Future<String>. |
| _checkIsSubtypeMatchOf(futureOrType(T), futureType(stringType), [T], |
| ['T <: String', 'T <: Future<String>'], |
| covariant: true); |
| } |
| |
| void test_lhs_null() { |
| // Null <: T is trivially satisfied by the constraint Null <: T. |
| _checkIsSubtypeMatchOf(nullType, T, [T], ['Null <: T'], covariant: false); |
| // For any other type X, Null <: X is satisfied without the need for any |
| // constraints. |
| _checkOrdinarySubtypeMatch(nullType, listType(T), [T], covariant: false); |
| _checkOrdinarySubtypeMatch(nullType, stringType, [T], covariant: false); |
| _checkOrdinarySubtypeMatch(nullType, voidType, [T], covariant: false); |
| _checkOrdinarySubtypeMatch(nullType, dynamicType, [T], covariant: false); |
| _checkOrdinarySubtypeMatch(nullType, objectType, [T], covariant: false); |
| _checkOrdinarySubtypeMatch(nullType, nullType, [T], covariant: false); |
| _checkOrdinarySubtypeMatch( |
| nullType, |
| _functionType(required: [intType], returns: stringType), |
| [T], |
| covariant: false, |
| ); |
| } |
| |
| void test_param_on_lhs_contravariant_direct() { |
| // When doing a contravariant match, the type parameters we're trying to |
| // find types for are on the right hand side. Is a type parameter also |
| // appears on the left hand side, there is a condition in which the |
| // constraint can be satisfied without consulting the bound of the LHS type |
| // parameter: the condition where both parameters appear at corresponding |
| // locations in the type tree. |
| // |
| // In other words, List<S> <: List<T> is satisfied provided that |
| // S <: T. |
| var S = _typeParameterType( |
| _typeParameter('S'), |
| ); |
| _checkIsSubtypeMatchOf(listType(S), listType(T), [T], ['S <: T'], |
| covariant: false); |
| } |
| |
| void test_param_on_lhs_contravariant_via_bound() { |
| // When doing a contravariant match, the type parameters we're trying to |
| // find types for are on the right hand side. Is a type parameter also |
| // appears on the left hand side, we may have to constrain the RHS type |
| // parameter using the bounds of the LHS type parameter. |
| // |
| // In other words, S <: List<T> is satisfied provided that |
| // bound(S) <: List<T>. |
| var S = _typeParameterType( |
| _typeParameter( |
| 'S', |
| bound: listType(stringType), |
| ), |
| ); |
| _checkIsSubtypeMatchOf(S, listType(T), [T], ['String <: T'], |
| covariant: false); |
| } |
| |
| void test_param_on_lhs_covariant() { |
| // When doing a covariant match, the type parameters we're trying to find |
| // types for are on the left hand side. |
| _checkIsSubtypeMatchOf(T, stringType, [T], ['T <: String'], |
| covariant: true); |
| } |
| |
| void test_param_on_rhs_contravariant() { |
| // When doing a contravariant match, the type parameters we're trying to |
| // find types for are on the right hand side. |
| _checkIsSubtypeMatchOf(stringType, T, [T], ['String <: T'], |
| covariant: false); |
| } |
| |
| void test_param_on_rhs_covariant_match() { |
| // When doing a covariant match, the type parameters we're trying to find |
| // types for are on the left hand side. If a type parameter appears on the |
| // right hand side, there is a condition in which the constraint can be |
| // satisfied: where both parameters appear at corresponding locations in the |
| // type tree. |
| // |
| // In other words, T <: S can be satisfied trivially by the constraint |
| // T <: S. |
| var S = _typeParameterType( |
| _typeParameter('S'), |
| ); |
| _checkIsSubtypeMatchOf(T, S, [T], ['T <: S'], covariant: true); |
| } |
| |
| void test_param_on_rhs_covariant_no_match() { |
| // When doing a covariant match, the type parameters we're trying to find |
| // types for are on the left hand side. If a type parameter appears on the |
| // right hand side, it's probable that the constraint can't be satisfied, |
| // because there is no possible type for the LHS (other than bottom) |
| // that's guaranteed to satisfy the relation for all possible assignments of |
| // the RHS type parameter. |
| // |
| // In other words, no match can be found for List<T> <: S because regardless |
| // of T, we can't guarantee that List<T> <: S for all S. |
| var S = _typeParameterType( |
| _typeParameter('S'), |
| ); |
| _checkIsNotSubtypeMatchOf(listType(T), S, [T], covariant: true); |
| } |
| |
| void test_related_interface_types_failure() { |
| _checkIsNotSubtypeMatchOf(iterableType(T), listType(stringType), [T], |
| covariant: true); |
| } |
| |
| void test_related_interface_types_success() { |
| _checkIsSubtypeMatchOf( |
| listType(T), iterableType(stringType), [T], ['T <: String'], |
| covariant: true); |
| } |
| |
| void test_rhs_dynamic() { |
| // T <: dynamic is trivially satisfied by the constraint T <: dynamic. |
| _checkIsSubtypeMatchOf(T, dynamicType, [T], ['T <: dynamic'], |
| covariant: true); |
| // For any other type X, X <: dynamic is satisfied without the need for any |
| // constraints. |
| _checkOrdinarySubtypeMatch(listType(T), dynamicType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(stringType, dynamicType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(voidType, dynamicType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(dynamicType, dynamicType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(objectType, dynamicType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(nullType, dynamicType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch( |
| _functionType(required: [intType], returns: stringType), |
| dynamicType, |
| [T], |
| covariant: true, |
| ); |
| } |
| |
| void test_rhs_object() { |
| // T <: Object is trivially satisfied by the constraint T <: Object. |
| _checkIsSubtypeMatchOf(T, objectType, [T], ['T <: Object'], |
| covariant: true); |
| // For any other type X, X <: Object is satisfied without the need for any |
| // constraints. |
| _checkOrdinarySubtypeMatch(listType(T), objectType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(stringType, objectType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(voidType, objectType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(dynamicType, objectType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(objectType, objectType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(nullType, objectType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch( |
| _functionType(required: [intType], returns: stringType), |
| objectType, |
| [T], |
| covariant: true, |
| ); |
| } |
| |
| void test_rhs_void() { |
| // T <: void is trivially satisfied by the constraint T <: void. |
| _checkIsSubtypeMatchOf(T, voidType, [T], ['T <: void'], covariant: true); |
| // For any other type X, X <: void is satisfied without the need for any |
| // constraints. |
| _checkOrdinarySubtypeMatch(listType(T), voidType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(stringType, voidType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(voidType, voidType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(dynamicType, voidType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(objectType, voidType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(nullType, voidType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch( |
| _functionType(required: [intType], returns: stringType), |
| voidType, |
| [T], |
| covariant: true, |
| ); |
| } |
| |
| void test_same_interface_types() { |
| _checkIsSubtypeMatchOf( |
| listType(T), listType(stringType), [T], ['T <: String'], |
| covariant: true); |
| } |
| |
| void test_x_futureOr_fail_both_branches() { |
| // List<T> <: FutureOr<String> can't be satisfied because neither |
| // List<T> <: Future<String> nor List<T> <: int can be satisfied |
| _checkIsNotSubtypeMatchOf(listType(T), futureOrType(stringType), [T], |
| covariant: true); |
| } |
| |
| void test_x_futureOr_pass_both_branches_constraints_from_both_branches() { |
| // Future<String> <: FutureOr<T> can be satisfied because both |
| // Future<String> <: Future<T> and Future<String> <: T can be satisfied. |
| // Trying to match Future<String> <: Future<T> generates the constraint |
| // String <: T, whereas trying to match Future<String> <: T generates the |
| // constraint Future<String> <: T. We keep the constraint based on trying |
| // to match Future<String> <: Future<T>, so String <: T. |
| _checkIsSubtypeMatchOf( |
| futureType(stringType), futureOrType(T), [T], ['String <: T'], |
| covariant: false); |
| } |
| |
| void test_x_futureOr_pass_both_branches_constraints_from_future_branch() { |
| // Future<T> <: FutureOr<Object> can be satisfied because both |
| // Future<T> <: Future<Object> and Future<T> <: Object can be satisfied. |
| // Trying to match Future<T> <: Future<Object> generates the constraint |
| // T <: Object, whereas trying to match Future<T> <: Object generates no |
| // constraints, so we keep the constraint T <: Object. |
| _checkIsSubtypeMatchOf( |
| futureType(T), futureOrType(objectType), [T], ['T <: Object'], |
| covariant: true); |
| } |
| |
| void test_x_futureOr_pass_both_branches_constraints_from_nonFuture_branch() { |
| // Null <: FutureOr<T> can be satisfied because both |
| // Null <: Future<T> and Null <: T can be satisfied. |
| // Trying to match Null <: FutureOr<T> generates no constraints, whereas |
| // trying to match Null <: T generates the constraint Null <: T, |
| // so we keep the constraint Null <: T. |
| _checkIsSubtypeMatchOf(nullType, futureOrType(T), [T], ['Null <: T'], |
| covariant: false); |
| } |
| |
| void test_x_futureOr_pass_both_branches_no_constraints() { |
| // Future<String> <: FutureOr<Object> is satisfied because both |
| // Future<String> <: Future<Object> and Future<String> <: Object. |
| // No constraints are recorded. |
| _checkIsSubtypeMatchOf( |
| futureType(stringType), futureOrType(objectType), [T], [], |
| covariant: true); |
| } |
| |
| void test_x_futureOr_pass_future_branch() { |
| // Future<T> <: FutureOr<String> can be satisfied because |
| // Future<T> <: Future<String> can be satisfied |
| _checkIsSubtypeMatchOf( |
| futureType(T), futureOrType(stringType), [T], ['T <: String'], |
| covariant: true); |
| } |
| |
| void test_x_futureOr_pass_nonFuture_branch() { |
| // List<T> <: FutureOr<List<String>> can be satisfied because |
| // List<T> <: List<String> can be satisfied |
| _checkIsSubtypeMatchOf( |
| listType(T), futureOrType(listType(stringType)), [T], ['T <: String'], |
| covariant: true); |
| } |
| |
| void _checkIsNotSubtypeMatchOf( |
| DartType t1, DartType t2, Iterable<TypeParameterType> typeFormals, |
| {bool covariant}) { |
| var inferrer = new GenericInferrer( |
| typeProvider, typeSystem, typeFormals.map((t) => t.element)); |
| var success = |
| inferrer.tryMatchSubtypeOf(t1, t2, null, covariant: covariant); |
| expect(success, isFalse); |
| inferrer.constraints.forEach((typeParameter, constraintsForTypeParameter) { |
| expect(constraintsForTypeParameter, isEmpty); |
| }); |
| } |
| |
| void _checkIsSubtypeMatchOf( |
| DartType t1, |
| DartType t2, |
| Iterable<TypeParameterType> typeFormals, |
| Iterable<String> expectedConstraints, |
| {bool covariant}) { |
| var inferrer = new GenericInferrer( |
| typeProvider, typeSystem, typeFormals.map((t) => t.element)); |
| var success = |
| inferrer.tryMatchSubtypeOf(t1, t2, null, covariant: covariant); |
| expect(success, isTrue); |
| var formattedConstraints = <String>[]; |
| inferrer.constraints.forEach((typeParameter, constraintsForTypeParameter) { |
| for (var constraint in constraintsForTypeParameter) { |
| formattedConstraints.add(constraint.format(typeParameter.toString())); |
| } |
| }); |
| expect(formattedConstraints, unorderedEquals(expectedConstraints)); |
| } |
| |
| void _checkOrdinarySubtypeMatch( |
| DartType t1, DartType t2, Iterable<TypeParameterType> typeFormals, |
| {bool covariant}) { |
| bool expectSuccess = typeSystem.isSubtypeOf(t1, t2); |
| if (expectSuccess) { |
| _checkIsSubtypeMatchOf(t1, t2, typeFormals, [], covariant: covariant); |
| } else { |
| _checkIsNotSubtypeMatchOf(t1, t2, typeFormals); |
| } |
| } |
| } |
| |
| @reflectiveTest |
| class GenericFunctionInferenceTest extends AbstractTypeSystemTest { |
| void test_boundedByAnotherTypeParameter() { |
| // <TFrom, TTo extends Iterable<TFrom>>(TFrom) -> TTo |
| var tFrom = _typeParameter('TFrom'); |
| var tTo = |
| _typeParameter('TTo', bound: iterableType(_typeParameterType(tFrom))); |
| var cast = _functionType( |
| typeFormals: [tFrom, tTo], |
| required: [_typeParameterType(tFrom)], |
| returns: _typeParameterType(tTo), |
| ); |
| expect( |
| _inferCall(cast, [stringType]), [stringType, iterableType(stringType)]); |
| } |
| |
| void test_boundedByOuterClass() { |
| // Regression test for https://github.com/dart-lang/sdk/issues/25740. |
| |
| // class A {} |
| var A = _class(name: 'A', superType: objectType); |
| var typeA = _interfaceType(A); |
| |
| // class B extends A {} |
| var B = _class(name: 'B', superType: typeA); |
| var typeB = _interfaceType(B); |
| |
| // class C<T extends A> { |
| var CT = _typeParameter('T', bound: typeA); |
| var C = _class( |
| name: 'C', |
| superType: objectType, |
| typeParameters: [CT], |
| ); |
| // S m<S extends T>(S); |
| var S = _typeParameter('S', bound: _typeParameterType(CT)); |
| var m = _method( |
| 'm', |
| _typeParameterType(S), |
| typeFormals: [S], |
| parameters: [_requiredParameter('_', _typeParameterType(S))], |
| ); |
| C.methods = [m]; |
| // } |
| |
| // C<Object> cOfObject; |
| var cOfObject = _interfaceType(C, typeArguments: [objectType]); |
| // C<A> cOfA; |
| var cOfA = _interfaceType(C, typeArguments: [typeA]); |
| // C<B> cOfB; |
| var cOfB = _interfaceType(C, typeArguments: [typeB]); |
| // B b; |
| // cOfB.m(b); // infer <B> |
| expect(_inferCall2(cOfB.getMethod('m').type, [typeB]).toString(), |
| 'B Function(B)'); |
| // cOfA.m(b); // infer <B> |
| expect(_inferCall2(cOfA.getMethod('m').type, [typeB]).toString(), |
| 'B Function(B)'); |
| // cOfObject.m(b); // infer <B> |
| expect(_inferCall2(cOfObject.getMethod('m').type, [typeB]).toString(), |
| 'B Function(B)'); |
| } |
| |
| void test_boundedByOuterClassSubstituted() { |
| // Regression test for https://github.com/dart-lang/sdk/issues/25740. |
| |
| // class A {} |
| var A = _class(name: 'A', superType: objectType); |
| var typeA = _interfaceType(A); |
| |
| // class B extends A {} |
| var B = _class(name: 'B', superType: typeA); |
| var typeB = _interfaceType(B); |
| |
| // class C<T extends A> { |
| var CT = _typeParameter('T', bound: typeA); |
| var C = _class( |
| name: 'C', |
| superType: objectType, |
| typeParameters: [CT], |
| ); |
| // S m<S extends Iterable<T>>(S); |
| var iterableOfT = iterableType(_typeParameterType(CT)); |
| var S = _typeParameter('S', bound: iterableOfT); |
| var m = _method( |
| 'm', |
| _typeParameterType(S), |
| typeFormals: [S], |
| parameters: [_requiredParameter('_', _typeParameterType(S))], |
| ); |
| C.methods = [m]; |
| // } |
| |
| // C<Object> cOfObject; |
| var cOfObject = _interfaceType(C, typeArguments: [objectType]); |
| // C<A> cOfA; |
| var cOfA = _interfaceType(C, typeArguments: [typeA]); |
| // C<B> cOfB; |
| var cOfB = _interfaceType(C, typeArguments: [typeB]); |
| // List<B> b; |
| var listOfB = listType(typeB); |
| // cOfB.m(b); // infer <B> |
| expect(_inferCall2(cOfB.getMethod('m').type, [listOfB]).toString(), |
| 'List<B> Function(List<B>)'); |
| // cOfA.m(b); // infer <B> |
| expect(_inferCall2(cOfA.getMethod('m').type, [listOfB]).toString(), |
| 'List<B> Function(List<B>)'); |
| // cOfObject.m(b); // infer <B> |
| expect(_inferCall2(cOfObject.getMethod('m').type, [listOfB]).toString(), |
| 'List<B> Function(List<B>)'); |
| } |
| |
| void test_boundedRecursively() { |
| // class A<T extends A<T>> |
| var T = _typeParameter('T'); |
| var A = _class( |
| name: 'Cloneable', |
| superType: objectType, |
| typeParameters: [T], |
| ); |
| T.bound = _interfaceType( |
| A, |
| typeArguments: [_typeParameterType(T)], |
| ); |
| |
| // class B extends A<B> {} |
| var B = _class(name: 'B', superType: null); |
| B.supertype = _interfaceType(A, typeArguments: [_interfaceType(B)]); |
| var typeB = _interfaceType(B); |
| |
| // <S extends A<S>> |
| var S = _typeParameter('S'); |
| var typeS = _typeParameterType(S); |
| S.bound = _interfaceType(A, typeArguments: [typeS]); |
| |
| // (S, S) -> S |
| var clone = _functionType( |
| typeFormals: [S], |
| required: [typeS, typeS], |
| returns: typeS, |
| ); |
| expect(_inferCall(clone, [typeB, typeB]), [typeB]); |
| |
| // Something invalid... |
| expect( |
| _inferCall(clone, [stringType, numType], expectError: true), |
| [objectType], |
| ); |
| } |
| |
| void test_genericCastFunction() { |
| // <TFrom, TTo>(TFrom) -> TTo |
| var tFrom = _typeParameter('TFrom'); |
| var tTo = _typeParameter('TTo'); |
| var cast = _functionType( |
| typeFormals: [tFrom, tTo], |
| required: [_typeParameterType(tFrom)], |
| returns: _typeParameterType(tTo), |
| ); |
| expect(_inferCall(cast, [intType]), [intType, dynamicType]); |
| } |
| |
| void test_genericCastFunctionWithUpperBound() { |
| // <TFrom, TTo extends TFrom>(TFrom) -> TTo |
| var tFrom = _typeParameter('TFrom'); |
| var tTo = _typeParameter('TTo', bound: _typeParameterType(tFrom)); |
| var cast = _functionType( |
| typeFormals: [tFrom, tTo], |
| required: [_typeParameterType(tFrom)], |
| returns: _typeParameterType(tTo), |
| ); |
| expect(_inferCall(cast, [intType]), [intType, intType]); |
| } |
| |
| void test_parametersToFunctionParam() { |
| // <T>(f(T t)) -> T |
| var T = _typeParameter('T'); |
| var cast = _functionType( |
| typeFormals: [T], |
| required: [ |
| _functionType( |
| required: [_typeParameterType(T)], |
| returns: dynamicType, |
| ) |
| ], |
| returns: _typeParameterType(T), |
| ); |
| expect( |
| _inferCall(cast, [ |
| _functionType( |
| required: [numType], |
| returns: dynamicType, |
| ) |
| ]), |
| [numType], |
| ); |
| } |
| |
| void test_parametersUseLeastUpperBound() { |
| // <T>(T x, T y) -> T |
| var T = _typeParameter('T'); |
| var cast = _functionType( |
| typeFormals: [T], |
| required: [ |
| _typeParameterType(T), |
| _typeParameterType(T), |
| ], |
| returns: _typeParameterType(T), |
| ); |
| expect(_inferCall(cast, [intType, doubleType]), [numType]); |
| } |
| |
| void test_parameterTypeUsesUpperBound() { |
| // <T extends num>(T) -> dynamic |
| var T = _typeParameter('T', bound: numType); |
| var f = _functionType( |
| typeFormals: [T], |
| required: [ |
| _typeParameterType(T), |
| ], |
| returns: dynamicType, |
| ); |
| expect(_inferCall(f, [intType]), [intType]); |
| } |
| |
| void test_returnFunctionWithGenericParameter() { |
| // <T>(T -> T) -> (T -> void) |
| var T = _typeParameter('T'); |
| var f = _functionType( |
| typeFormals: [T], |
| required: [ |
| _functionType( |
| required: [ |
| _typeParameterType(T), |
| ], |
| returns: _typeParameterType(T), |
| ) |
| ], |
| returns: _functionType( |
| required: [ |
| _typeParameterType(T), |
| ], |
| returns: voidType, |
| ), |
| ); |
| expect( |
| _inferCall(f, [ |
| _functionType(required: [numType], returns: intType) |
| ]), |
| [intType], |
| ); |
| } |
| |
| void test_returnFunctionWithGenericParameterAndContext() { |
| // <T>(T -> T) -> (T -> Null) |
| var T = _typeParameter('T'); |
| var f = _functionType( |
| typeFormals: [T], |
| required: [ |
| _functionType( |
| required: [ |
| _typeParameterType(T), |
| ], |
| returns: _typeParameterType(T), |
| ) |
| ], |
| returns: _functionType( |
| required: [ |
| _typeParameterType(T), |
| ], |
| returns: nullType, |
| ), |
| ); |
| expect( |
| _inferCall( |
| f, |
| [], |
| returnType: _functionType( |
| required: [numType], |
| returns: intType, |
| ), |
| ), |
| [numType], |
| ); |
| } |
| |
| void test_returnFunctionWithGenericParameterAndReturn() { |
| // <T>(T -> T) -> (T -> T) |
| var T = _typeParameter('T'); |
| var f = _functionType( |
| typeFormals: [T], |
| required: [ |
| _functionType( |
| required: [ |
| _typeParameterType(T), |
| ], |
| returns: _typeParameterType(T), |
| ) |
| ], |
| returns: _functionType( |
| required: [ |
| _typeParameterType(T), |
| ], |
| returns: _typeParameterType(T), |
| ), |
| ); |
| expect( |
| _inferCall(f, [ |
| _functionType( |
| required: [numType], |
| returns: intType, |
| ) |
| ]), |
| [intType], |
| ); |
| } |
| |
| void test_returnFunctionWithGenericReturn() { |
| // <T>(T -> T) -> (() -> T) |
| var T = _typeParameter('T'); |
| var f = _functionType( |
| typeFormals: [T], |
| required: [ |
| _functionType( |
| required: [ |
| _typeParameterType(T), |
| ], |
| returns: _typeParameterType(T), |
| ) |
| ], |
| returns: _functionType( |
| returns: _typeParameterType(T), |
| ), |
| ); |
| expect( |
| _inferCall(f, [ |
| _functionType( |
| required: [numType], |
| returns: intType, |
| ) |
| ]), |
| [intType], |
| ); |
| } |
| |
| void test_returnTypeFromContext() { |
| // <T>() -> T |
| var T = _typeParameter('T'); |
| var f = _functionType( |
| typeFormals: [T], |
| returns: _typeParameterType(T), |
| ); |
| expect(_inferCall(f, [], returnType: stringType), [stringType]); |
| } |
| |
| void test_returnTypeWithBoundFromContext() { |
| // <T extends num>() -> T |
| var T = _typeParameter('T', bound: numType); |
| var f = _functionType( |
| typeFormals: [T], |
| returns: _typeParameterType(T), |
| ); |
| expect(_inferCall(f, [], returnType: doubleType), [doubleType]); |
| } |
| |
| void test_returnTypeWithBoundFromInvalidContext() { |
| // <T extends num>() -> T |
| var T = _typeParameter('T', bound: numType); |
| var f = _functionType( |
| typeFormals: [T], |
| returns: _typeParameterType(T), |
| ); |
| expect(_inferCall(f, [], returnType: stringType), [nullType]); |
| } |
| |
| void test_unifyParametersToFunctionParam() { |
| // <T>(f(T t), g(T t)) -> T |
| var T = _typeParameter('T'); |
| var cast = _functionType( |
| typeFormals: [T], |
| required: [ |
| _functionType( |
| required: [_typeParameterType(T)], |
| returns: dynamicType, |
| ), |
| _functionType( |
| required: [_typeParameterType(T)], |
| returns: dynamicType, |
| ) |
| ], |
| returns: _typeParameterType(T), |
| ); |
| expect( |
| _inferCall(cast, [ |
| _functionType(required: [intType], returns: dynamicType), |
| _functionType(required: [doubleType], returns: dynamicType) |
| ]), |
| [nullType], |
| ); |
| } |
| |
| void test_unusedReturnTypeIsDynamic() { |
| // <T>() -> T |
| var T = _typeParameter('T'); |
| var f = _functionType( |
| typeFormals: [T], |
| returns: _typeParameterType(T), |
| ); |
| expect(_inferCall(f, []), [dynamicType]); |
| } |
| |
| void test_unusedReturnTypeWithUpperBound() { |
| // <T extends num>() -> T |
| var T = _typeParameter('T', bound: numType); |
| var f = _functionType( |
| typeFormals: [T], |
| returns: _typeParameterType(T), |
| ); |
| expect(_inferCall(f, []), [numType]); |
| } |
| |
| List<DartType> _inferCall(FunctionTypeImpl ft, List<DartType> arguments, |
| {DartType returnType, bool expectError: false}) { |
| var listener = new RecordingErrorListener(); |
| |
| var reporter = new ErrorReporter( |
| listener, |
| new NonExistingSource( |
| '/test.dart', toUri('/test.dart'), UriKind.FILE_URI)); |
| |
| var typeArguments = typeSystem.inferGenericFunctionOrType( |
| typeParameters: ft.typeFormals, |
| parameters: ft.parameters, |
| declaredReturnType: ft.returnType, |
| argumentTypes: arguments, |
| contextReturnType: returnType, |
| errorReporter: reporter, |
| errorNode: astFactory.nullLiteral(new KeywordToken(Keyword.NULL, 0)), |
| ); |
| |
| if (expectError) { |
| expect(listener.errors.map((e) => e.errorCode).toList(), |
| [StrongModeCode.COULD_NOT_INFER], |
| reason: 'expected exactly 1 could not infer error.'); |
| } else { |
| expect(listener.errors, isEmpty, reason: 'did not expect any errors.'); |
| } |
| return typeArguments; |
| } |
| |
| FunctionType _inferCall2(FunctionTypeImpl ft, List<DartType> arguments, |
| {DartType returnType, bool expectError: false}) { |
| var typeArguments = _inferCall( |
| ft, |
| arguments, |
| returnType: returnType, |
| expectError: expectError, |
| ); |
| return ft.instantiate(typeArguments); |
| } |
| } |
| |
| @reflectiveTest |
| class GreatestLowerBoundTest extends BoundTestBase { |
| void test_bottom_function() { |
| _checkGreatestLowerBound(bottomType, _functionType(), bottomType); |
| } |
| |
| void test_bottom_interface() { |
| var A = _class(name: 'A'); |
| _checkGreatestLowerBound(bottomType, _interfaceType(A), bottomType); |
| } |
| |
| void test_bottom_typeParam() { |
| var T = _typeParameter('T'); |
| _checkGreatestLowerBound(bottomType, _typeParameterType(T), bottomType); |
| } |
| |
| void test_bounds_of_top_types_complete() { |
| // Test every combination of a subset of Tops programatically. |
| var futureOrDynamicType = futureOrType(dynamicType); |
| var futureOrObjectType = futureOrType(objectType); |
| var futureOrVoidType = futureOrType(voidType); |
| final futureOrFutureOrDynamicType = futureOrType(futureOrDynamicType); |
| final futureOrFutureOrObjectType = futureOrType(futureOrObjectType); |
| final futureOrFutureOrVoidType = futureOrType(futureOrVoidType); |
| |
| var orderedTops = [ |
| // Lower index, so lower Top |
| voidType, |
| dynamicType, |
| objectType, |
| futureOrVoidType, |
| futureOrDynamicType, |
| futureOrObjectType, |
| futureOrFutureOrVoidType, |
| futureOrFutureOrDynamicType, |
| futureOrFutureOrObjectType, |
| // Higher index, higher Top |
| ]; |
| |
| // We could sort and check the sort result is correct in O(n log n), but a |
| // good sorting algorithm would only run n tests here (that each value is |
| // correct relative to its nearest neighbors). But O(n^2) for n=6 is stupid |
| // fast, in this case, so just do the brute force check because we can. |
| for (var i = 0; i < orderedTops.length; ++i) { |
| for (var lower = 0; lower <= i; ++lower) { |
| _checkGreatestLowerBound( |
| orderedTops[i], orderedTops[lower], orderedTops[i]); |
| _checkLeastUpperBound( |
| orderedTops[i], orderedTops[lower], orderedTops[lower]); |
| } |
| for (var greater = i; greater < orderedTops.length; ++greater) { |
| _checkGreatestLowerBound( |
| orderedTops[i], orderedTops[greater], orderedTops[greater]); |
| _checkLeastUpperBound( |
| orderedTops[i], orderedTops[greater], orderedTops[i]); |
| } |
| } |
| } |
| |
| void test_bounds_of_top_types_sanity() { |
| var futureOrDynamicType = futureOrType(dynamicType); |
| final futureOrFutureOrDynamicType = futureOrType(futureOrDynamicType); |
| |
| // Sanity check specific cases of top for GLB/LUB. |
| _checkLeastUpperBound(objectType, dynamicType, dynamicType); |
| _checkGreatestLowerBound(objectType, dynamicType, objectType); |
| _checkLeastUpperBound(objectType, voidType, voidType); |
| _checkLeastUpperBound(futureOrDynamicType, dynamicType, dynamicType); |
| _checkGreatestLowerBound( |
| futureOrDynamicType, objectType, futureOrDynamicType); |
| _checkGreatestLowerBound(futureOrDynamicType, futureOrFutureOrDynamicType, |
| futureOrFutureOrDynamicType); |
| } |
| |
| void test_classAndSuperclass() { |
| // class A |
| // class B extends A |
| // class C extends B |
| var A = _class(name: 'A'); |
| var B = _class(name: 'B', superType: _interfaceType(A)); |
| var C = _class(name: 'C', superType: _interfaceType(B)); |
| _checkGreatestLowerBound( |
| _interfaceType(A), |
| _interfaceType(C), |
| _interfaceType(C), |
| ); |
| } |
| |
| void test_classAndSuperinterface() { |
| // class A |
| // class B implements A |
| // class C implements B |
| var A = _class(name: 'A'); |
| var B = _class(name: 'B', interfaces: [_interfaceType(A)]); |
| var C = _class(name: 'C', interfaces: [_interfaceType(B)]); |
| _checkGreatestLowerBound( |
| _interfaceType(A), |
| _interfaceType(C), |
| _interfaceType(C), |
| ); |
| } |
| |
| void test_dynamic_bottom() { |
| _checkGreatestLowerBound(dynamicType, bottomType, bottomType); |
| } |
| |
| void test_dynamic_function() { |
| _checkGreatestLowerBound(dynamicType, _functionType(), _functionType()); |
| } |
| |
| void test_dynamic_interface() { |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| _checkGreatestLowerBound(dynamicType, typeA, typeA); |
| } |
| |
| void test_dynamic_typeParam() { |
| var T = _typeParameter('T'); |
| var typeT = _typeParameterType(T); |
| _checkGreatestLowerBound(dynamicType, typeT, typeT); |
| } |
| |
| void test_dynamic_void() { |
| // Note: _checkGreatestLowerBound tests `GLB(x, y)` as well as `GLB(y, x)` |
| _checkGreatestLowerBound(dynamicType, voidType, dynamicType); |
| } |
| |
| void test_functionsDifferentNamedTakeUnion() { |
| var type1 = _functionType( |
| named: {'a': intType, 'b': intType}, |
| ); |
| var type2 = _functionType( |
| named: {'b': doubleType, 'c': stringType}, |
| ); |
| var expected = _functionType( |
| named: {'a': intType, 'b': numType, 'c': stringType}, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsDifferentOptionalArityTakeMax() { |
| var type1 = _functionType( |
| optional: [intType], |
| ); |
| var type2 = _functionType( |
| optional: [doubleType, stringType, objectType], |
| ); |
| var expected = _functionType( |
| optional: [numType, stringType, objectType], |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsDifferentRequiredArityBecomeOptional() { |
| var type1 = _functionType( |
| required: [intType], |
| ); |
| var type2 = _functionType( |
| required: [intType, intType, intType], |
| ); |
| var expected = _functionType( |
| required: [intType], |
| optional: [intType, intType], |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsFromDynamic() { |
| var type1 = _functionType(required: [dynamicType]); |
| var type2 = _functionType(required: [intType]); |
| var expected = _functionType(required: [dynamicType]); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsGlbReturnType() { |
| var type1 = _functionType(returns: intType); |
| var type2 = _functionType(returns: numType); |
| var expected = _functionType(returns: intType); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsLubNamedParams() { |
| var type1 = _functionType( |
| named: {'a': stringType, 'b': intType}, |
| ); |
| var type2 = _functionType( |
| named: {'a': intType, 'b': numType}, |
| ); |
| var expected = _functionType( |
| named: {'a': objectType, 'b': numType}, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsLubPositionalParams() { |
| var type1 = _functionType( |
| optional: [stringType, intType], |
| ); |
| var type2 = _functionType( |
| optional: [intType, numType], |
| ); |
| var expected = _functionType( |
| optional: [objectType, numType], |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsLubRequiredParams() { |
| var type1 = _functionType( |
| required: [stringType, intType, intType], |
| ); |
| var type2 = _functionType( |
| required: [intType, doubleType, numType], |
| ); |
| var expected = _functionType( |
| required: [objectType, numType, numType], |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsMixedOptionalAndRequiredBecomeOptional() { |
| var type1 = _functionType( |
| required: [intType, intType], |
| optional: [intType, intType, intType], |
| ); |
| var type2 = _functionType( |
| required: [intType], |
| optional: [intType, intType], |
| ); |
| var expected = _functionType( |
| required: [intType], |
| optional: [intType, intType, intType, intType], |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsReturnBottomIfMixOptionalAndNamed() { |
| // Dart doesn't allow a function to have both optional and named parameters, |
| // so if we would have synthethized that, pick bottom instead. |
| var type1 = _functionType( |
| required: [intType], |
| named: {'a': intType}, |
| ); |
| var type2 = _functionType( |
| named: {'a': intType}, |
| ); |
| _checkGreatestLowerBound(type1, type2, bottomType); |
| } |
| |
| void test_functionsSameType_withNamed() { |
| var type1 = _functionType( |
| required: [stringType, intType, numType], |
| named: {'n': numType}, |
| returns: intType, |
| ); |
| |
| var type2 = _functionType( |
| required: [stringType, intType, numType], |
| named: {'n': numType}, |
| returns: intType, |
| ); |
| |
| var expected = _functionType( |
| required: [stringType, intType, numType], |
| named: {'n': numType}, |
| returns: intType, |
| ); |
| |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsSameType_withOptional() { |
| var type1 = _functionType( |
| required: [stringType, intType, numType], |
| optional: [doubleType], |
| returns: intType, |
| ); |
| |
| var type2 = _functionType( |
| required: [stringType, intType, numType], |
| optional: [doubleType], |
| returns: intType, |
| ); |
| |
| var expected = _functionType( |
| required: [stringType, intType, numType], |
| optional: [doubleType], |
| returns: intType, |
| ); |
| |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_interface_function() { |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| _checkGreatestLowerBound(typeA, _functionType(), bottomType); |
| } |
| |
| void test_mixin() { |
| // class A |
| // class B |
| // class C |
| // class D extends A with B, C |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| |
| var B = _class(name: 'B'); |
| var typeB = _interfaceType(B); |
| |
| var C = _class(name: 'C'); |
| var typeC = _interfaceType(C); |
| |
| var D = _class( |
| name: 'D', |
| superType: _interfaceType(A), |
| mixins: [typeB, typeC], |
| ); |
| var typeD = _interfaceType(D); |
| |
| _checkGreatestLowerBound(typeA, typeD, typeD); |
| _checkGreatestLowerBound(typeB, typeD, typeD); |
| _checkGreatestLowerBound(typeC, typeD, typeD); |
| } |
| |
| void test_self() { |
| var T = _typeParameter('T'); |
| var A = _class(name: 'A'); |
| |
| List<DartType> types = [ |
| dynamicType, |
| voidType, |
| bottomType, |
| _typeParameterType(T), |
| _interfaceType(A), |
| _functionType(), |
| ]; |
| |
| for (DartType type in types) { |
| _checkGreatestLowerBound(type, type, type); |
| } |
| } |
| |
| void test_typeParam_function_noBound() { |
| var T = _typeParameter('T'); |
| _checkGreatestLowerBound( |
| _typeParameterType(T), |
| _functionType(), |
| bottomType, |
| ); |
| } |
| |
| void test_typeParam_interface_bounded() { |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| |
| var B = _class(name: 'B', superType: typeA); |
| var typeB = _interfaceType(B); |
| |
| var C = _class(name: 'C', superType: typeB); |
| var typeC = _interfaceType(C); |
| |
| var T = _typeParameter('T', bound: typeB); |
| _checkGreatestLowerBound(_typeParameterType(T), typeC, bottomType); |
| } |
| |
| void test_typeParam_interface_noBound() { |
| // GLB(T, A) = ⊥ |
| var T = _typeParameter('T'); |
| var A = _class(name: 'A'); |
| _checkGreatestLowerBound( |
| _typeParameterType(T), |
| _interfaceType(A), |
| bottomType, |
| ); |
| } |
| |
| void test_typeParameters_different() { |
| // GLB(List<int>, List<double>) = ⊥ |
| var listOfIntType = listType(intType); |
| var listOfDoubleType = listType(doubleType); |
| // TODO(rnystrom): Can we do something better here? |
| _checkGreatestLowerBound(listOfIntType, listOfDoubleType, bottomType); |
| } |
| |
| void test_typeParameters_same() { |
| // GLB(List<int>, List<int>) = List<int> |
| var listOfIntType = listType(intType); |
| _checkGreatestLowerBound(listOfIntType, listOfIntType, listOfIntType); |
| } |
| |
| void test_unrelatedClasses() { |
| // class A |
| // class B |
| // class C |
| var A = _class(name: 'A'); |
| var B = _class(name: 'B'); |
| _checkGreatestLowerBound(_interfaceType(A), _interfaceType(B), bottomType); |
| } |
| |
| void test_void() { |
| var A = _class(name: 'A'); |
| var T = _typeParameter('T'); |
| List<DartType> types = [ |
| bottomType, |
| _functionType(), |
| _interfaceType(A), |
| _typeParameterType(T), |
| ]; |
| for (DartType type in types) { |
| _checkGreatestLowerBound( |
| _functionType(returns: voidType), |
| _functionType(returns: type), |
| _functionType(returns: type), |
| ); |
| } |
| } |
| } |
| |
| @reflectiveTest |
| class LeastUpperBoundFunctionsTest extends BoundTestBase { |
| void test_differentRequiredArity() { |
| var type1 = _functionType(required: [intType, intType]); |
| var type2 = _functionType(required: [intType, intType, intType]); |
| _checkLeastUpperBound(type1, type2, functionType); |
| } |
| |
| void test_fuzzyArrows() { |
| var type1 = _functionType(required: [dynamicType]); |
| var type2 = _functionType(required: [intType]); |
| var expected = _functionType(required: [intType]); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_glbNamedParams() { |
| var type1 = _functionType( |
| named: {'a': stringType, 'b': intType}, |
| ); |
| var type2 = _functionType( |
| named: {'a': intType, 'b': numType}, |
| ); |
| var expected = _functionType( |
| named: {'a': bottomType, 'b': intType}, |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_glbPositionalParams() { |
| var type1 = _functionType( |
| optional: [stringType, intType], |
| ); |
| var type2 = _functionType( |
| optional: [intType, numType], |
| ); |
| var expected = _functionType( |
| optional: [bottomType, intType], |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_glbRequiredParams() { |
| var type1 = _functionType( |
| required: [stringType, intType, intType], |
| ); |
| var type2 = _functionType( |
| required: [intType, doubleType, numType], |
| ); |
| var expected = _functionType( |
| required: [bottomType, bottomType, intType], |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_ignoreExtraNamedParams() { |
| var type1 = _functionType( |
| named: {'a': intType, 'b': intType}, |
| ); |
| var type2 = _functionType( |
| named: {'a': intType, 'c': intType}, |
| ); |
| var expected = _functionType( |
| named: {'a': intType}, |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_ignoreExtraPositionalParams() { |
| var type1 = _functionType( |
| optional: [intType, intType, stringType], |
| ); |
| var type2 = _functionType( |
| optional: [intType], |
| ); |
| var expected = _functionType( |
| optional: [intType], |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_lubReturnType() { |
| var type1 = _functionType(returns: intType); |
| var type2 = _functionType(returns: doubleType); |
| var expected = _functionType(returns: numType); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_sameType_withNamed() { |
| var type1 = _functionType( |
| required: [stringType, intType, numType], |
| named: {'n': numType}, |
| returns: intType, |
| ); |
| |
| var type2 = _functionType( |
| required: [stringType, intType, numType], |
| named: {'n': numType}, |
| returns: intType, |
| ); |
| |
| var expected = _functionType( |
| required: [stringType, intType, numType], |
| named: {'n': numType}, |
| returns: intType, |
| ); |
| |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_sameType_withOptional() { |
| var type1 = _functionType( |
| required: [stringType, intType, numType], |
| optional: [doubleType], |
| returns: intType, |
| ); |
| |
| var type2 = _functionType( |
| required: [stringType, intType, numType], |
| optional: [doubleType], |
| returns: intType, |
| ); |
| |
| var expected = _functionType( |
| required: [stringType, intType, numType], |
| optional: [doubleType], |
| returns: intType, |
| ); |
| |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_typeFormals_differentBounds() { |
| var T1 = _typeParameter('T1', bound: intType); |
| var type1 = _functionType( |
| typeFormals: [T1], |
| returns: _typeParameterType(T1), |
| ); |
| |
| var T2 = _typeParameter('T2', bound: doubleType); |
| var type2 = _functionType( |
| typeFormals: [T2], |
| returns: _typeParameterType(T2), |
| ); |
| |
| _checkLeastUpperBound(type1, type2, functionType); |
| } |
| |
| void test_typeFormals_differentNumber() { |
| var T1 = _typeParameter('T1', bound: numType); |
| var type1 = _functionType( |
| typeFormals: [T1], |
| returns: _typeParameterType(T1), |
| ); |
| |
| var type2 = _functionType(returns: intType); |
| |
| _checkLeastUpperBound(type1, type2, functionType); |
| } |
| |
| void test_typeFormals_sameBounds() { |
| var T1 = _typeParameter('T1', bound: numType); |
| var type1 = _functionType( |
| typeFormals: [T1], |
| returns: _typeParameterType(T1), |
| ); |
| |
| var T2 = _typeParameter('T2', bound: numType); |
| var type2 = _functionType( |
| typeFormals: [T2], |
| returns: _typeParameterType(T2), |
| ); |
| |
| var TE = _typeParameter('T', bound: numType); |
| var expected = _functionType( |
| typeFormals: [TE], |
| returns: _typeParameterType(TE), |
| ); |
| |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| } |
| |
| @reflectiveTest |
| class LeastUpperBoundTest extends BoundTestBase { |
| void test_bottom_function() { |
| _checkLeastUpperBound(bottomType, _functionType(), _functionType()); |
| } |
| |
| void test_bottom_interface() { |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| _checkLeastUpperBound(bottomType, typeA, typeA); |
| } |
| |
| void test_bottom_typeParam() { |
| var T = _typeParameter('T'); |
| var typeT = _typeParameterType(T); |
| _checkLeastUpperBound(bottomType, typeT, typeT); |
| } |
| |
| void test_directInterfaceCase() { |
| // class A |
| // class B implements A |
| // class C implements B |
| |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| |
| var B = _class(name: 'B', interfaces: [typeA]); |
| var typeB = _interfaceType(B); |
| |
| var C = _class(name: 'C', interfaces: [typeB]); |
| var typeC = _interfaceType(C); |
| |
| _checkLeastUpperBound(typeB, typeC, typeB); |
| } |
| |
| void test_directSubclassCase() { |
| // class A |
| // class B extends A |
| // class C extends B |
| |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| |
| var B = _class(name: 'B', superType: typeA); |
| var typeB = _interfaceType(B); |
| |
| var C = _class(name: 'C', superType: typeB); |
| var typeC = _interfaceType(C); |
| |
| _checkLeastUpperBound(typeB, typeC, typeB); |
| } |
| |
| void test_directSuperclass_nullability() { |
| var aElement = _class(name: 'A'); |
| var aQuestion = _interfaceType( |
| aElement, |
| nullabilitySuffix: NullabilitySuffix.question, |
| ); |
| var aStar = _interfaceType( |
| aElement, |
| nullabilitySuffix: NullabilitySuffix.star, |
| ); |
| var aNone = _interfaceType( |
| aElement, |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| |
| var bElementStar = _class(name: 'B', superType: aStar); |
| var bElementNone = _class(name: 'B', superType: aNone); |
| |
| InterfaceTypeImpl _bTypeStarElement(NullabilitySuffix nullability) { |
| return _interfaceType( |
| bElementStar, |
| nullabilitySuffix: nullability, |
| ); |
| } |
| |
| InterfaceTypeImpl _bTypeNoneElement(NullabilitySuffix nullability) { |
| return _interfaceType( |
| bElementNone, |
| nullabilitySuffix: nullability, |
| ); |
| } |
| |
| var bStarQuestion = _bTypeStarElement(NullabilitySuffix.question); |
| var bStarStar = _bTypeStarElement(NullabilitySuffix.star); |
| var bStarNone = _bTypeStarElement(NullabilitySuffix.none); |
| |
| var bNoneQuestion = _bTypeNoneElement(NullabilitySuffix.question); |
| var bNoneStar = _bTypeNoneElement(NullabilitySuffix.star); |
| var bNoneNone = _bTypeNoneElement(NullabilitySuffix.none); |
| |
| void assertLUB(DartType type1, DartType type2, DartType expected) { |
| expect(typeSystem.getLeastUpperBound(type1, type2), expected); |
| expect(typeSystem.getLeastUpperBound(type2, type1), expected); |
| } |
| |
| assertLUB(bStarQuestion, aQuestion, aQuestion); |
| assertLUB(bStarQuestion, aStar, aQuestion); |
| assertLUB(bStarQuestion, aNone, aQuestion); |
| |
| assertLUB(bStarStar, aQuestion, aQuestion); |
| assertLUB(bStarStar, aStar, aStar); |
| assertLUB(bStarStar, aNone, aStar); |
| |
| assertLUB(bStarNone, aQuestion, aQuestion); |
| assertLUB(bStarNone, aStar, aStar); |
| assertLUB(bStarNone, aNone, aNone); |
| |
| assertLUB(bNoneQuestion, aQuestion, aQuestion); |
| assertLUB(bNoneQuestion, aStar, aQuestion); |
| assertLUB(bNoneQuestion, aNone, aQuestion); |
| |
| assertLUB(bNoneStar, aQuestion, aQuestion); |
| assertLUB(bNoneStar, aStar, aStar); |
| assertLUB(bNoneStar, aNone, aStar); |
| |
| assertLUB(bNoneNone, aQuestion, aQuestion); |
| assertLUB(bNoneNone, aStar, aStar); |
| assertLUB(bNoneNone, aNone, aNone); |
| } |
| |
| void test_dynamic_bottom() { |
| _checkLeastUpperBound(dynamicType, bottomType, dynamicType); |
| } |
| |
| void test_dynamic_function() { |
| _checkLeastUpperBound(dynamicType, _functionType(), dynamicType); |
| } |
| |
| void test_dynamic_interface() { |
| var A = _class(name: 'A'); |
| _checkLeastUpperBound(dynamicType, _interfaceType(A), dynamicType); |
| } |
| |
| void test_dynamic_typeParam() { |
| var T = _typeParameter('T'); |
| _checkLeastUpperBound(dynamicType, _typeParameterType(T), dynamicType); |
| } |
| |
| void test_dynamic_void() { |
| // Note: _checkLeastUpperBound tests `LUB(x, y)` as well as `LUB(y, x)` |
| _checkLeastUpperBound(dynamicType, voidType, voidType); |
| } |
| |
| void test_interface_function() { |
| var A = _class(name: 'A'); |
| _checkLeastUpperBound(_interfaceType(A), _functionType(), objectType); |
| } |
| |
| void test_interface_sameElement_nullability() { |
| var aElement = _class(name: 'A'); |
| |
| var aQuestion = _interfaceType( |
| aElement, |
| nullabilitySuffix: NullabilitySuffix.question, |
| ); |
| var aStar = _interfaceType( |
| aElement, |
| nullabilitySuffix: NullabilitySuffix.star, |
| ); |
| var aNone = _interfaceType( |
| aElement, |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| |
| void assertLUB(DartType type1, DartType type2, DartType expected) { |
| expect(typeSystem.getLeastUpperBound(type1, type2), expected); |
| expect(typeSystem.getLeastUpperBound(type2, type1), expected); |
| } |
| |
| assertLUB(aQuestion, aQuestion, aQuestion); |
| assertLUB(aQuestion, aStar, aQuestion); |
| assertLUB(aQuestion, aNone, aQuestion); |
| |
| assertLUB(aStar, aQuestion, aQuestion); |
| assertLUB(aStar, aStar, aStar); |
| assertLUB(aStar, aNone, aStar); |
| |
| assertLUB(aNone, aQuestion, aQuestion); |
| assertLUB(aNone, aStar, aStar); |
| assertLUB(aNone, aNone, aNone); |
| } |
| |
| void test_mixinAndClass_constraintAndInterface() { |
| var classA = _class(name: 'A'); |
| var instA = InstantiatedClass(classA, []); |
| |
| var classB = _class( |
| name: 'B', |
| interfaces: [instA.withNullabilitySuffixNone], |
| ); |
| |
| var mixinM = ElementFactory.mixinElement( |
| name: 'M', |
| constraints: [instA.withNullabilitySuffixNone], |
| ); |
| |
| _checkLeastUpperBound( |
| _interfaceType( |
| classB, |
| nullabilitySuffix: NullabilitySuffix.star, |
| ), |
| _interfaceType( |
| mixinM, |
| nullabilitySuffix: NullabilitySuffix.star, |
| ), |
| instA.withNullability(NullabilitySuffix.star), |
| ); |
| } |
| |
| void test_mixinAndClass_object() { |
| var classA = _class(name: 'A'); |
| var mixinM = ElementFactory.mixinElement(name: 'M'); |
| |
| _checkLeastUpperBound( |
| _interfaceType(classA), |
| _interfaceType(mixinM), |
| objectType, |
| ); |
| } |
| |
| void test_mixinAndClass_sharedInterface() { |
| var classA = _class(name: 'A'); |
| var instA = InstantiatedClass(classA, []); |
| |
| var classB = _class( |
| name: 'B', |
| interfaces: [instA.withNullabilitySuffixNone], |
| ); |
| |
| var mixinM = ElementFactory.mixinElement( |
| name: 'M', |
| interfaces: [instA.withNullabilitySuffixNone], |
| ); |
| |
| _checkLeastUpperBound( |
| _interfaceType( |
| classB, |
| nullabilitySuffix: NullabilitySuffix.star, |
| ), |
| _interfaceType( |
| mixinM, |
| nullabilitySuffix: NullabilitySuffix.star, |
| ), |
| instA.withNullability(NullabilitySuffix.star), |
| ); |
| } |
| |
| void test_mixinCase() { |
| // class A |
| // class B extends A |
| // class C extends A |
| // class D extends B with M, N, O, P |
| |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| |
| var B = _class(name: 'B', superType: typeA); |
| var typeB = _interfaceType(B); |
| |
| var C = _class(name: 'C', superType: typeA); |
| var typeC = _interfaceType(C); |
| |
| var D = _class( |
| name: 'D', |
| superType: typeB, |
| mixins: [ |
| _interfaceType(_class(name: 'M')), |
| _interfaceType(_class(name: 'N')), |
| _interfaceType(_class(name: 'O')), |
| _interfaceType(_class(name: 'P')), |
| ], |
| ); |
| var typeD = _interfaceType(D); |
| |
| _checkLeastUpperBound(typeD, typeC, typeA); |
| } |
| |
| void test_nestedFunctionsLubInnerParamTypes() { |
| var type1 = _functionType( |
| required: [ |
| _functionType(required: [stringType, intType, intType]) |
| ], |
| ); |
| var type2 = _functionType( |
| required: [ |
| _functionType(required: [intType, doubleType, numType]) |
| ], |
| ); |
| var expected = _functionType( |
| required: [ |
| _functionType(required: [objectType, numType, numType]) |
| ], |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_nestedNestedFunctionsGlbInnermostParamTypes() { |
| var type1 = _functionType(required: [ |
| _functionType(required: [ |
| _functionType(required: [stringType, intType, intType]) |
| ]) |
| ]); |
| var type2 = _functionType(required: [ |
| _functionType(required: [ |
| _functionType(required: [intType, doubleType, numType]) |
| ]) |
| ]); |
| var expected = _functionType(required: [ |
| _functionType(required: [ |
| _functionType(required: [bottomType, bottomType, intType]) |
| ]) |
| ]); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_object() { |
| var A = _class(name: 'A'); |
| var B = _class(name: 'B'); |
| var typeA = _interfaceType(A); |
| var typeB = _interfaceType(B); |
| var typeObject = typeA.element.supertype; |
| // assert that object does not have a super type |
| expect(typeObject.element.supertype, isNull); |
| // assert that both A and B have the same super type of Object |
| expect(typeB.element.supertype, typeObject); |
| // finally, assert that the only least upper bound of A and B is Object |
| _checkLeastUpperBound(typeA, typeB, typeObject); |
| } |
| |
| void test_self() { |
| var T = _typeParameter('T'); |
| var A = _class(name: 'A'); |
| |
| List<DartType> types = [ |
| dynamicType, |
| voidType, |
| bottomType, |
| _typeParameterType(T), |
| _interfaceType(A), |
| _functionType() |
| ]; |
| |
| for (DartType type in types) { |
| _checkLeastUpperBound(type, type, type); |
| } |
| } |
| |
| void test_sharedSuperclass1() { |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| |
| var B = _class(name: 'B', superType: typeA); |
| var typeB = _interfaceType(B); |
| |
| var C = _class(name: 'C', superType: typeA); |
| var typeC = _interfaceType(C); |
| |
| _checkLeastUpperBound(typeB, typeC, typeA); |
| } |
| |
| void test_sharedSuperclass1_nullability() { |
| var aElement = _class(name: 'A'); |
| var aQuestion = _interfaceType( |
| aElement, |
| nullabilitySuffix: NullabilitySuffix.question, |
| ); |
| var aStar = _interfaceType( |
| aElement, |
| nullabilitySuffix: NullabilitySuffix.star, |
| ); |
| var aNone = _interfaceType( |
| aElement, |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| |
| var bElementNone = _class(name: 'B', superType: aNone); |
| var bElementStar = _class(name: 'B', superType: aStar); |
| |
| var cElementNone = _class(name: 'C', superType: aNone); |
| var cElementStar = _class(name: 'C', superType: aStar); |
| |
| InterfaceTypeImpl bTypeElementNone(NullabilitySuffix nullability) { |
| return _interfaceType( |
| bElementNone, |
| nullabilitySuffix: nullability, |
| ); |
| } |
| |
| InterfaceTypeImpl bTypeElementStar(NullabilitySuffix nullability) { |
| return _interfaceType( |
| bElementStar, |
| nullabilitySuffix: nullability, |
| ); |
| } |
| |
| var bNoneQuestion = bTypeElementNone(NullabilitySuffix.question); |
| var bNoneStar = bTypeElementNone(NullabilitySuffix.star); |
| var bNoneNone = bTypeElementNone(NullabilitySuffix.none); |
| |
| var bStarQuestion = bTypeElementStar(NullabilitySuffix.question); |
| var bStarStar = bTypeElementStar(NullabilitySuffix.star); |
| var bStarNone = bTypeElementStar(NullabilitySuffix.none); |
| |
| InterfaceTypeImpl cTypeElementNone(NullabilitySuffix nullability) { |
| return _interfaceType( |
| cElementNone, |
| nullabilitySuffix: nullability, |
| ); |
| } |
| |
| InterfaceTypeImpl cTypeElementStar(NullabilitySuffix nullability) { |
| return _interfaceType( |
| cElementStar, |
| nullabilitySuffix: nullability, |
| ); |
| } |
| |
| var cNoneQuestion = cTypeElementNone(NullabilitySuffix.question); |
| var cNoneStar = cTypeElementNone(NullabilitySuffix.star); |
| var cNoneNone = cTypeElementNone(NullabilitySuffix.none); |
| |
| var cStarQuestion = cTypeElementStar(NullabilitySuffix.question); |
| var cStarStar = cTypeElementStar(NullabilitySuffix.star); |
| var cStarNone = cTypeElementStar(NullabilitySuffix.none); |
| |
| void assertLUB(DartType type1, DartType type2, DartType expected) { |
| expect(typeSystem.getLeastUpperBound(type1, type2), expected); |
| expect(typeSystem.getLeastUpperBound(type2, type1), expected); |
| } |
| |
| assertLUB(bNoneQuestion, cNoneQuestion, aQuestion); |
| assertLUB(bNoneQuestion, cNoneStar, aQuestion); |
| assertLUB(bNoneQuestion, cNoneNone, aQuestion); |
| assertLUB(bNoneQuestion, cStarQuestion, aQuestion); |
| assertLUB(bNoneQuestion, cStarStar, aQuestion); |
| assertLUB(bNoneQuestion, cStarNone, aQuestion); |
| |
| assertLUB(bNoneStar, cNoneQuestion, aQuestion); |
| assertLUB(bNoneStar, cNoneStar, aStar); |
| assertLUB(bNoneStar, cNoneNone, aStar); |
| assertLUB(bNoneStar, cStarQuestion, aQuestion); |
| assertLUB(bNoneStar, cStarStar, aStar); |
| assertLUB(bNoneStar, cStarNone, aStar); |
| |
| assertLUB(bNoneNone, cNoneQuestion, aQuestion); |
| assertLUB(bNoneNone, cNoneStar, aStar); |
| assertLUB(bNoneNone, cNoneNone, aNone); |
| assertLUB(bNoneNone, cStarQuestion, aQuestion); |
| assertLUB(bNoneNone, cStarStar, aStar); |
| assertLUB(bNoneNone, cStarNone, aNone); |
| |
| assertLUB(bStarQuestion, cNoneQuestion, aQuestion); |
| assertLUB(bStarQuestion, cNoneStar, aQuestion); |
| assertLUB(bStarQuestion, cNoneNone, aQuestion); |
| assertLUB(bStarQuestion, cStarQuestion, aQuestion); |
| assertLUB(bStarQuestion, cStarStar, aQuestion); |
| assertLUB(bStarQuestion, cStarNone, aQuestion); |
| |
| assertLUB(bStarStar, cNoneQuestion, aQuestion); |
| assertLUB(bStarStar, cNoneStar, aStar); |
| assertLUB(bStarStar, cNoneNone, aStar); |
| assertLUB(bStarStar, cStarQuestion, aQuestion); |
| assertLUB(bStarStar, cStarStar, aStar); |
| assertLUB(bStarStar, cStarNone, aStar); |
| |
| assertLUB(bStarNone, cNoneQuestion, aQuestion); |
| assertLUB(bStarNone, cNoneStar, aStar); |
| assertLUB(bStarNone, cNoneNone, aNone); |
| assertLUB(bStarNone, cStarQuestion, aQuestion); |
| assertLUB(bStarNone, cStarStar, aStar); |
| assertLUB(bStarNone, cStarNone, aNone); |
| } |
| |
| void test_sharedSuperclass2() { |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| |
| var B = _class(name: 'B', superType: typeA); |
| var typeB = _interfaceType(B); |
| |
| var C = _class(name: 'C', superType: typeA); |
| var typeC = _interfaceType(C); |
| |
| var D = _class(name: 'D', superType: typeC); |
| var typeD = _interfaceType(D); |
| |
| _checkLeastUpperBound(typeB, typeD, typeA); |
| } |
| |
| void test_sharedSuperclass3() { |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| |
| var B = _class(name: 'B', superType: typeA); |
| var typeB = _interfaceType(B); |
| |
| var C = _class(name: 'C', superType: typeB); |
| var typeC = _interfaceType(C); |
| |
| var D = _class(name: 'D', superType: typeB); |
| var typeD = _interfaceType(D); |
| |
| _checkLeastUpperBound(typeC, typeD, typeB); |
| } |
| |
| void test_sharedSuperclass4() { |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| |
| var A2 = _class(name: 'A2'); |
| var typeA2 = _interfaceType(A2); |
| |
| var A3 = _class(name: 'A3'); |
| var typeA3 = _interfaceType(A3); |
| |
| var B = _class(name: 'B', superType: typeA, interfaces: [typeA2]); |
| var typeB = _interfaceType(B); |
| |
| var C = _class(name: 'C', superType: typeA, interfaces: [typeA3]); |
| var typeC = _interfaceType(C); |
| |
| _checkLeastUpperBound(typeB, typeC, typeA); |
| } |
| |
| void test_sharedSuperinterface1() { |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| |
| var B = _class(name: 'B', interfaces: [typeA]); |
| var typeB = _interfaceType(B); |
| |
| var C = _class(name: 'C', interfaces: [typeA]); |
| var typeC = _interfaceType(C); |
| |
| _checkLeastUpperBound(typeB, typeC, typeA); |
| } |
| |
| void test_sharedSuperinterface2() { |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| |
| var B = _class(name: 'B', interfaces: [typeA]); |
| var typeB = _interfaceType(B); |
| |
| var C = _class(name: 'C', interfaces: [typeA]); |
| var typeC = _interfaceType(C); |
| |
| var D = _class(name: 'D', interfaces: [typeC]); |
| var typeD = _interfaceType(D); |
| |
| _checkLeastUpperBound(typeB, typeD, typeA); |
| } |
| |
| void test_sharedSuperinterface3() { |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| |
| var B = _class(name: 'B', interfaces: [typeA]); |
| var typeB = _interfaceType(B); |
| |
| var C = _class(name: 'C', interfaces: [typeB]); |
| var typeC = _interfaceType(C); |
| |
| var D = _class(name: 'D', interfaces: [typeB]); |
| var typeD = _interfaceType(D); |
| |
| _checkLeastUpperBound(typeC, typeD, typeB); |
| } |
| |
| void test_sharedSuperinterface4() { |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| |
| var A2 = _class(name: 'A2'); |
| var typeA2 = _interfaceType(A2); |
| |
| var A3 = _class(name: 'A3'); |
| var typeA3 = _interfaceType(A3); |
| |
| var B = _class(name: 'B', interfaces: [typeA, typeA2]); |
| var typeB = _interfaceType(B); |
| |
| var C = _class(name: 'C', interfaces: [typeA, typeA3]); |
| var typeC = _interfaceType(C); |
| |
| _checkLeastUpperBound(typeB, typeC, typeA); |
| } |
| |
| void test_twoComparables() { |
| _checkLeastUpperBound(stringType, numType, objectType); |
| } |
| |
| void test_typeParam_boundedByParam() { |
| var S = _typeParameter('S'); |
| var typeS = _typeParameterType(S); |
| |
| var T = _typeParameter('T', bound: typeS); |
| var typeT = _typeParameterType(T); |
| |
| _checkLeastUpperBound(typeT, typeS, typeS); |
| } |
| |
| void test_typeParam_class_implements_Function_ignored() { |
| var A = _class(name: 'A', superType: functionType); |
| var T = _typeParameter('T', bound: _interfaceType(A)); |
| _checkLeastUpperBound(_typeParameterType(T), _functionType(), objectType); |
| } |
| |
| void test_typeParam_fBounded() { |
| var T = _typeParameter('Q'); |
| var A = _class(name: 'A', typeParameters: [T]); |
| |
| var S = _typeParameter('S'); |
| var typeS = _typeParameterType(S); |
| S.bound = _interfaceType(A, typeArguments: [typeS]); |
| |
| var U = _typeParameter('U'); |
| var typeU = _typeParameterType(U); |
| U.bound = _interfaceType(A, typeArguments: [typeU]); |
| |
| _checkLeastUpperBound( |
| typeS, |
| _typeParameterType(U), |
| _interfaceType(A, typeArguments: [objectType]), |
| ); |
| } |
| |
| void test_typeParam_function_bounded() { |
| var T = _typeParameter('T', bound: functionType); |
| _checkLeastUpperBound(_typeParameterType(T), _functionType(), functionType); |
| } |
| |
| void test_typeParam_function_noBound() { |
| var T = _typeParameter('T'); |
| _checkLeastUpperBound(_typeParameterType(T), _functionType(), objectType); |
| } |
| |
| void test_typeParam_interface_bounded() { |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| |
| var B = _class(name: 'B', superType: typeA); |
| var typeB = _interfaceType(B); |
| |
| var C = _class(name: 'C', superType: typeA); |
| var typeC = _interfaceType(C); |
| |
| var T = _typeParameter('T', bound: typeB); |
| var typeT = _typeParameterType(T); |
| |
| _checkLeastUpperBound(typeT, typeC, typeA); |
| } |
| |
| void test_typeParam_interface_noBound() { |
| var T = _typeParameter('T'); |
| var A = _class(name: 'A'); |
| _checkLeastUpperBound( |
| _typeParameterType(T), |
| _interfaceType(A), |
| objectType, |
| ); |
| } |
| |
| /// Check least upper bound of the same class with different type parameters. |
| void test_typeParameters_different() { |
| // class List<int> |
| // class List<double> |
| var listOfIntType = listType(intType); |
| var listOfDoubleType = listType(doubleType); |
| var listOfNum = listType(numType); |
| _checkLeastUpperBound(listOfIntType, listOfDoubleType, listOfNum); |
| } |
| |
| void test_typeParameters_same() { |
| // List<int> |
| // List<int> |
| var listOfIntType = listType(intType); |
| _checkLeastUpperBound(listOfIntType, listOfIntType, listOfIntType); |
| } |
| |
| /// Check least upper bound of two related classes with different |
| /// type parameters. |
| void test_typeParametersAndClass_different() { |
| // class List<int> |
| // class Iterable<double> |
| var listOfIntType = listType(intType); |
| var iterableOfDoubleType = iterableType(doubleType); |
| // TODO(leafp): this should be iterableOfNumType |
| _checkLeastUpperBound(listOfIntType, iterableOfDoubleType, objectType); |
| } |
| |
| void test_void() { |
| List<DartType> types = [ |
| bottomType, |
| _functionType(), |
| _interfaceType(_class(name: 'A')), |
| _typeParameterType(_typeParameter('T')), |
| ]; |
| for (DartType type in types) { |
| _checkLeastUpperBound( |
| _functionType(returns: voidType), |
| _functionType(returns: type), |
| _functionType(returns: voidType), |
| ); |
| } |
| } |
| } |
| |
| @reflectiveTest |
| class NonNullableSubtypingTest extends SubtypingTestBase { |
| @override |
| FeatureSet get testFeatureSet { |
| return FeatureSet.forTesting( |
| additionalFeatures: [Feature.non_nullable], |
| ); |
| } |
| |
| void test_dynamicType() { |
| List<DartType> equivalents = <DartType>[ |
| voidType, |
| _question(objectType), |
| _star(objectType), |
| ]; |
| List<DartType> subtypes = <DartType>[bottomType, nullType, objectType]; |
| _checkGroups(dynamicType, equivalents: equivalents, subtypes: subtypes); |
| } |
| |
| @failingTest |
| void test_futureOr_topTypes() { |
| var objectStar = |
| (objectType as TypeImpl).withNullability(NullabilitySuffix.star); |
| var objectQuestion = |
| (objectType as TypeImpl).withNullability(NullabilitySuffix.question); |
| var futureOrObject = futureOrType(objectType); |
| var futureOrObjectStar = futureOrType(objectStar); |
| var futureOrObjectQuestion = futureOrType(objectQuestion); |
| var futureOrStarObject = |
| (futureOrObject as TypeImpl).withNullability(NullabilitySuffix.star); |
| var futureOrQuestionObject = (futureOrObject as TypeImpl) |
| .withNullability(NullabilitySuffix.question); |
| var futureOrStarObjectStar = (futureOrObjectStar as TypeImpl) |
| .withNullability(NullabilitySuffix.star); |
| var futureOrQuestionObjectStar = (futureOrObjectStar as TypeImpl) |
| .withNullability(NullabilitySuffix.question); |
| var futureOrStarObjectQuestion = (futureOrObjectQuestion as TypeImpl) |
| .withNullability(NullabilitySuffix.star); |
| var futureOrQuestionObjectQuestion = (futureOrObjectQuestion as TypeImpl) |
| .withNullability(NullabilitySuffix.question); |
| |
| //FutureOr<Object> <: FutureOr*<Object?> |
| _checkGroups(futureOrObject, equivalents: [ |
| objectStar, |
| futureOrObjectStar, |
| futureOrStarObject, |
| futureOrStarObjectStar, |
| objectType |
| ], subtypes: [], supertypes: [ |
| objectQuestion, |
| futureOrQuestionObject, |
| futureOrObjectQuestion, |
| futureOrQuestionObject, |
| futureOrQuestionObjectStar, |
| futureOrStarObjectQuestion, |
| futureOrQuestionObjectQuestion, |
| ]); |
| } |
| |
| void test_int_nullableTypes() { |
| List<DartType> equivalents = <DartType>[ |
| intType, |
| _star(intType), |
| ]; |
| List<DartType> subtypes = <DartType>[ |
| bottomType, |
| ]; |
| List<DartType> supertypes = <DartType>[ |
| _question(intType), |
| objectType, |
| _question(objectType), |
| ]; |
| List<DartType> unrelated = <DartType>[ |
| doubleType, |
| nullType, |
| _star(nullType), |
| _question(nullType), |
| _question(bottomType), |
| ]; |
| _checkGroups(intType, |
| equivalents: equivalents, |
| supertypes: supertypes, |
| unrelated: unrelated, |
| subtypes: subtypes); |
| } |
| |
| void test_intQuestion_nullableTypes() { |
| List<DartType> equivalents = <DartType>[ |
| _question(intType), |
| _star(intType), |
| ]; |
| List<DartType> subtypes = <DartType>[ |
| intType, |
| nullType, |
| _question(nullType), |
| _star(nullType), |
| bottomType, |
| _question(bottomType), |
| _star(bottomType), |
| ]; |
| List<DartType> supertypes = <DartType>[ |
| _question(numType), |
| _star(numType), |
| _question(objectType), |
| _star(objectType), |
| ]; |
| List<DartType> unrelated = <DartType>[doubleType, numType, objectType]; |
| _checkGroups(_question(intType), |
| equivalents: equivalents, |
| supertypes: supertypes, |
| unrelated: unrelated, |
| subtypes: subtypes); |
| } |
| |
| void test_intStar_nullableTypes() { |
| List<DartType> equivalents = <DartType>[ |
| intType, |
| _question(intType), |
| _star(intType), |
| ]; |
| List<DartType> subtypes = <DartType>[ |
| nullType, |
| _star(nullType), |
| _question(nullType), |
| bottomType, |
| _star(bottomType), |
| _question(bottomType), |
| ]; |
| List<DartType> supertypes = <DartType>[ |
| numType, |
| _question(numType), |
| _star(numType), |
| objectType, |
| _question(objectType), |
| ]; |
| List<DartType> unrelated = <DartType>[doubleType]; |
| _checkGroups(_star(intType), |
| equivalents: equivalents, |
| supertypes: supertypes, |
| unrelated: unrelated, |
| subtypes: subtypes); |
| } |
| |
| void test_nullType() { |
| List<DartType> equivalents = <DartType>[ |
| nullType, |
| _question(nullType), |
| _star(nullType), |
| _question(bottomType), |
| ]; |
| List<DartType> supertypes = <DartType>[ |
| _question(intType), |
| _star(intType), |
| _question(objectType), |
| _star(objectType), |
| dynamicType, |
| voidType, |
| ]; |
| List<DartType> subtypes = <DartType>[bottomType]; |
| List<DartType> unrelated = <DartType>[ |
| doubleType, |
| intType, |
| numType, |
| objectType, |
| ]; |
| |
| for (final formOfNull in equivalents) { |
| _checkGroups(formOfNull, |
| equivalents: equivalents, |
| supertypes: supertypes, |
| unrelated: unrelated, |
| subtypes: subtypes); |
| } |
| } |
| |
| void test_objectType() { |
| List<DartType> equivalents = <DartType>[ |
| _star(objectType), |
| ]; |
| List<DartType> supertypes = <DartType>[ |
| _question(objectType), |
| dynamicType, |
| voidType, |
| ]; |
| List<DartType> subtypes = <DartType>[bottomType]; |
| List<DartType> unrelated = <DartType>[ |
| _question(doubleType), |
| _question(numType), |
| _question(intType), |
| nullType, |
| ]; |
| _checkGroups(objectType, |
| equivalents: equivalents, |
| supertypes: supertypes, |
| unrelated: unrelated, |
| subtypes: subtypes); |
| } |
| |
| DartType _question(DartType dartType) => |
| (dartType as TypeImpl).withNullability(NullabilitySuffix.question); |
| |
| DartType _star(DartType dartType) => |
| (dartType as TypeImpl).withNullability(NullabilitySuffix.star); |
| } |
| |
| @reflectiveTest |
| class SubtypingTest extends SubtypingTestBase { |
| void test_bottom_isBottom() { |
| var A = _class(name: 'A'); |
| List<DartType> equivalents = <DartType>[bottomType]; |
| List<DartType> supertypes = <DartType>[ |
| dynamicType, |
| objectType, |
| intType, |
| doubleType, |
| numType, |
| stringType, |
| functionType, |
| _interfaceType(A), |
| ]; |
| _checkGroups(bottomType, equivalents: equivalents, supertypes: supertypes); |
| } |
| |
| void test_call_method() { |
| var A = _class(name: 'A', methods: [ |
| _method('call', objectType, parameters: [ |
| _requiredParameter('_', intType), |
| ]), |
| ]); |
| |
| _checkIsNotSubtypeOf( |
| _interfaceType(A), |
| _functionType(required: [intType], returns: objectType), |
| ); |
| } |
| |
| void test_classes() { |
| var A = _class(name: 'A'); |
| var typeA = _interfaceType(A); |
| |
| var B = _class(name: 'B', superType: typeA); |
| var typeB = _interfaceType(B); |
| |
| var C = _class(name: 'C', superType: typeA); |
| var typeC = _interfaceType(C); |
| |
| var D = _class( |
| name: 'D', |
| superType: _interfaceType(B), |
| interfaces: [typeC], |
| ); |
| var typeD = _interfaceType(D); |
| |
| _checkLattice(typeA, typeB, typeC, typeD); |
| } |
| |
| void test_double() { |
| List<DartType> equivalents = <DartType>[doubleType]; |
| List<DartType> supertypes = <DartType>[numType]; |
| List<DartType> unrelated = <DartType>[intType]; |
| _checkGroups(doubleType, |
| equivalents: equivalents, supertypes: supertypes, unrelated: unrelated); |
| } |
| |
| void test_dynamic_isTop() { |
| var A = _class(name: 'A'); |
| List<DartType> equivalents = <DartType>[dynamicType, objectType, voidType]; |
| List<DartType> subtypes = <DartType>[ |
| intType, |
| doubleType, |
| numType, |
| stringType, |
| functionType, |
| _interfaceType(A), |
| bottomType, |
| ]; |
| _checkGroups(dynamicType, equivalents: equivalents, subtypes: subtypes); |
| } |
| |
| void test_function_subtypes_itself_top_types() { |
| var tops = [dynamicType, objectType, voidType]; |
| // Add FutureOr<T> for T := dynamic, object, void |
| tops.addAll(tops.map((t) => futureOrType(t)).toList()); |
| // Add FutureOr<FutureOr<T>> for T := dynamic, object, void |
| tops.addAll(tops.skip(3).map((t) => futureOrType(t)).toList()); |
| |
| // Function should subtype all of those top types. |
| _checkGroups(functionType, supertypes: [ |
| dynamicType, |
| objectType, |
| voidType, |
| ]); |
| |
| // Create a non-identical but equal copy of Function, and verify subtyping |
| var copyOfFunction = _interfaceType(functionType.element); |
| _checkEquivalent(functionType, copyOfFunction); |
| } |
| |
| void test_genericFunction_generic_monomorphic() { |
| var S = _typeParameter('S'); |
| var T = _typeParameter('T', bound: _typeParameterType(S)); |
| var U = _typeParameter('U', bound: intType); |
| var V = _typeParameter('V', bound: _typeParameterType(U)); |
| |
| var A = _typeParameter('A'); |
| var B = _typeParameter('B', bound: _typeParameterType(A)); |
| var C = _typeParameter('C', bound: intType); |
| var D = _typeParameter('D', bound: _typeParameterType(C)); |
| |
| _checkIsStrictSubtypeOf( |
| _functionType( |
| typeFormals: [S, T], |
| required: [_typeParameterType(S)], |
| returns: _typeParameterType(T), |
| ), |
| _functionType( |
| typeFormals: [A, B], |
| required: [bottomType], |
| returns: dynamicType, |
| ), |
| ); |
| |
| _checkIsNotSubtypeOf( |
| _functionType( |
| typeFormals: [U, V], |
| required: [_typeParameterType(U)], |
| returns: _typeParameterType(V), |
| ), |
| _functionType( |
| typeFormals: [C, D], |
| required: [objectType], |
| returns: objectType, |
| ), |
| ); |
| |
| _checkIsNotSubtypeOf( |
| _functionType( |
| typeFormals: [U, V], |
| required: [_typeParameterType(U)], |
| returns: _typeParameterType(V), |
| ), |
| _functionType( |
| typeFormals: [C, D], |
| required: [intType], |
| returns: intType, |
| ), |
| ); |
| } |
| |
| void test_genericFunction_genericDoesNotSubtypeNonGeneric() { |
| var S = _typeParameter('S'); |
| var T = _typeParameter('T', bound: _typeParameterType(S)); |
| var U = _typeParameter('U', bound: intType); |
| var V = _typeParameter('V', bound: _typeParameterType(U)); |
| |
| _checkIsNotSubtypeOf( |
| _functionType( |
| typeFormals: [S, T], |
| required: [_typeParameterType(S)], |
| returns: _typeParameterType(T), |
| ), |
| _functionType(required: [dynamicType], returns: dynamicType), |
| ); |
| |
| _checkIsNotSubtypeOf( |
| _functionType( |
| typeFormals: [U, V], |
| required: [_typeParameterType(U)], |
| returns: _typeParameterType(V), |
| ), |
| _functionType(required: [objectType], returns: objectType), |
| ); |
| |
| _checkIsNotSubtypeOf( |
| _functionType( |
| typeFormals: [U, V], |
| required: [_typeParameterType(U)], |
| returns: _typeParameterType(V), |
| ), |
| _functionType(required: [intType], returns: intType), |
| ); |
| } |
| |
| void test_genericFunction_simple() { |
| var S = _typeParameter('S'); |
| var T = _typeParameter('T'); |
| |
| _checkEquivalent( |
| _functionType(typeFormals: [T]), |
| _functionType(typeFormals: [S]), |
| ); |
| |
| _checkEquivalent( |
| _functionType( |
| typeFormals: [T], |
| required: [_typeParameterType(T)], |
| returns: _typeParameterType(T), |
| ), |
| _functionType( |
| typeFormals: [S], |
| required: [_typeParameterType(S)], |
| returns: _typeParameterType(S), |
| ), |
| ); |
| } |
| |
| void test_genericFunction_simple_bounded() { |
| var S = _typeParameter('S'); |
| var T = _typeParameter('T', bound: _typeParameterType(S)); |
| var U = _typeParameter('U'); |
| var V = _typeParameter('V', bound: _typeParameterType(U)); |
| |
| _checkEquivalent( |
| _functionType(typeFormals: [S, T]), |
| _functionType(typeFormals: [U, V]), |
| ); |
| |
| _checkEquivalent( |
| _functionType( |
| typeFormals: [S, T], |
| required: [_typeParameterType(S)], |
| returns: _typeParameterType(T), |
| ), |
| _functionType( |
| typeFormals: [U, V], |
| required: [_typeParameterType(U)], |
| returns: _typeParameterType(V), |
| ), |
| ); |
| |
| { |
| var top = _functionType( |
| typeFormals: [S, T], |
| required: [_typeParameterType(T)], |
| returns: _typeParameterType(S), |
| ); |
| var left = _functionType( |
| typeFormals: [U, V], |
| required: [_typeParameterType(U)], |
| returns: _typeParameterType(U), |
| ); |
| var right = _functionType( |
| typeFormals: [U, V], |
| required: [_typeParameterType(V)], |
| returns: _typeParameterType(V), |
| ); |
| var bottom = _functionType( |
| typeFormals: [S, T], |
| required: [_typeParameterType(S)], |
| returns: _typeParameterType(T), |
| ); |
| _checkLattice(top, left, right, bottom); |
| } |
| } |
| |
| void test_generics() { |
| var LT = _typeParameter('T'); |
| var L = _class(name: 'L', typeParameters: [LT]); |
| |
| var MT = _typeParameter('T'); |
| var M = _class( |
| name: 'M', |
| typeParameters: [MT], |
| interfaces: [ |
| _interfaceType( |
| L, |
| typeArguments: [_typeParameterType(MT)], |
| ) |
| ], |
| ); |
| |
| var top = _interfaceType(L, typeArguments: [dynamicType]); |
| var left = _interfaceType(M, typeArguments: [dynamicType]); |
| var right = _interfaceType(L, typeArguments: [intType]); |
| var bottom = _interfaceType(M, typeArguments: [intType]); |
| |
| _checkLattice(top, left, right, bottom); |
| } |
| |
| void test_int() { |
| List<DartType> equivalents = <DartType>[intType]; |
| List<DartType> supertypes = <DartType>[numType]; |
| List<DartType> unrelated = <DartType>[doubleType]; |
| _checkGroups(intType, |
| equivalents: equivalents, supertypes: supertypes, unrelated: unrelated); |
| } |
| |
| void test_named_optional() { |
| var r = _functionType(required: [intType], returns: intType); |
| var o = _functionType(optional: [intType], returns: intType); |
| var n = _functionType(named: {'x': intType}, returns: intType); |
| |
| var rr = _functionType( |
| required: [intType, intType], |
| returns: intType, |
| ); |
| var ro = _functionType( |
| required: [intType], |
| optional: [intType], |
| returns: intType, |
| ); |
| var rn = _functionType( |
| required: [intType], |
| named: {'x': intType}, |
| returns: intType, |
| ); |
| var oo = _functionType( |
| optional: [intType, intType], |
| returns: intType, |
| ); |
| var nn = _functionType( |
| named: {'x': intType, 'y': intType}, |
| returns: intType, |
| ); |
| var nnn = _functionType( |
| named: {'x': intType, 'y': intType, 'z': intType}, |
| returns: intType, |
| ); |
| |
| _checkGroups(r, |
| equivalents: [r], |
| subtypes: [o, ro, rn, oo], |
| unrelated: [n, rr, nn, nnn]); |
| _checkGroups(o, |
| equivalents: [o], subtypes: [oo], unrelated: [n, rr, ro, rn, nn, nnn]); |
| _checkGroups(n, |
| equivalents: [n], |
| subtypes: [nn, nnn], |
| unrelated: [r, o, rr, ro, rn, oo]); |
| _checkGroups(rr, |
| equivalents: [rr], |
| subtypes: [ro, oo], |
| unrelated: [r, o, n, rn, nn, nnn]); |
| _checkGroups(ro, |
| equivalents: [ro], subtypes: [oo], unrelated: [o, n, rn, nn, nnn]); |
| _checkGroups(rn, |
| equivalents: [rn], |
| subtypes: [], |
| unrelated: [o, n, rr, ro, oo, nn, nnn]); |
| _checkGroups(oo, |
| equivalents: [oo], subtypes: [], unrelated: [n, rn, nn, nnn]); |
| _checkGroups(nn, |
| equivalents: [nn], subtypes: [nnn], unrelated: [r, o, rr, ro, rn, oo]); |
| _checkGroups(nnn, |
| equivalents: [nnn], subtypes: [], unrelated: [r, o, rr, ro, rn, oo]); |
| } |
| |
| void test_num() { |
| List<DartType> equivalents = <DartType>[numType]; |
| List<DartType> supertypes = <DartType>[]; |
| List<DartType> unrelated = <DartType>[stringType]; |
| List<DartType> subtypes = <DartType>[intType, doubleType]; |
| _checkGroups(numType, |
| equivalents: equivalents, |
| supertypes: supertypes, |
| unrelated: unrelated, |
| subtypes: subtypes); |
| } |
| |
| void test_simple_function() { |
| var top = _functionType(required: [intType], returns: objectType); |
| var left = _functionType(required: [intType], returns: intType); |
| var right = _functionType(required: [objectType], returns: objectType); |
| var bottom = _functionType(required: [objectType], returns: intType); |
| |
| _checkLattice(top, left, right, bottom); |
| } |
| |
| /// Regression test for https://github.com/dart-lang/sdk/issues/25069 |
| void test_simple_function_void() { |
| var functionType = _functionType(required: [intType], returns: objectType); |
| _checkIsNotSubtypeOf(voidType, functionType); |
| } |
| |
| void test_void_functions() { |
| var top = _functionType(required: [intType], returns: voidType); |
| var bottom = _functionType(required: [objectType], returns: intType); |
| |
| _checkIsStrictSubtypeOf(bottom, top); |
| } |
| |
| void test_void_isTop() { |
| var A = _class(name: 'A'); |
| List<DartType> equivalents = <DartType>[dynamicType, objectType, voidType]; |
| List<DartType> subtypes = <DartType>[ |
| intType, |
| doubleType, |
| numType, |
| stringType, |
| functionType, |
| _interfaceType(A), |
| bottomType |
| ]; |
| _checkGroups(voidType, equivalents: equivalents, subtypes: subtypes); |
| } |
| } |
| |
| class SubtypingTestBase extends AbstractTypeSystemTest { |
| void _checkEquivalent(DartType type1, DartType type2) { |
| _checkIsSubtypeOf(type1, type2); |
| _checkIsSubtypeOf(type2, type1); |
| } |
| |
| void _checkGroups(DartType t1, |
| {List<DartType> equivalents, |
| List<DartType> unrelated, |
| List<DartType> subtypes, |
| List<DartType> supertypes}) { |
| if (equivalents != null) { |
| for (DartType t2 in equivalents) { |
| _checkEquivalent(t1, t2); |
| } |
| } |
| if (unrelated != null) { |
| for (DartType t2 in unrelated) { |
| _checkUnrelated(t1, t2); |
| } |
| } |
| if (subtypes != null) { |
| for (DartType t2 in subtypes) { |
| _checkIsStrictSubtypeOf(t2, t1); |
| } |
| } |
| if (supertypes != null) { |
| for (DartType t2 in supertypes) { |
| _checkIsStrictSubtypeOf(t1, t2); |
| } |
| } |
| } |
| |
| void _checkIsNotSubtypeOf(DartType type1, DartType type2) { |
| expect(typeSystem.isSubtypeOf(type1, type2), false, |
| reason: '$type1 was not supposed to be a subtype of $type2'); |
| } |
| |
| void _checkIsStrictSubtypeOf(DartType type1, DartType type2) { |
| _checkIsSubtypeOf(type1, type2); |
| _checkIsNotSubtypeOf(type2, type1); |
| } |
| |
| void _checkIsSubtypeOf(DartType type1, DartType type2) { |
| expect(typeSystem.isSubtypeOf(type1, type2), true, |
| reason: '$type1 is not a subtype of $type2'); |
| } |
| |
| void _checkLattice( |
| DartType top, DartType left, DartType right, DartType bottom) { |
| _checkGroups(top, |
| equivalents: <DartType>[top], |
| subtypes: <DartType>[left, right, bottom]); |
| _checkGroups(left, |
| equivalents: <DartType>[left], |
| subtypes: <DartType>[bottom], |
| unrelated: <DartType>[right], |
| supertypes: <DartType>[top]); |
| _checkGroups(right, |
| equivalents: <DartType>[right], |
| subtypes: <DartType>[bottom], |
| unrelated: <DartType>[left], |
| supertypes: <DartType>[top]); |
| _checkGroups(bottom, |
| equivalents: <DartType>[bottom], |
| supertypes: <DartType>[top, left, right]); |
| } |
| |
| void _checkUnrelated(DartType type1, DartType type2) { |
| _checkIsNotSubtypeOf(type1, type2); |
| _checkIsNotSubtypeOf(type2, type1); |
| } |
| } |
| |
| @reflectiveTest |
| class TypeSystemTest extends AbstractTypeSystemTest { |
| InterfaceTypeImpl get functionClassTypeNone { |
| return _interfaceType( |
| typeProvider.functionType.element, |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| |
| InterfaceTypeImpl get functionClassTypeQuestion { |
| return _interfaceType( |
| typeProvider.functionType.element, |
| nullabilitySuffix: NullabilitySuffix.question, |
| ); |
| } |
| |
| InterfaceTypeImpl get functionClassTypeStar { |
| return _interfaceType( |
| typeProvider.functionType.element, |
| nullabilitySuffix: NullabilitySuffix.star, |
| ); |
| } |
| |
| DartType get noneType => (typeProvider.stringType as TypeImpl) |
| .withNullability(NullabilitySuffix.none); |
| |
| FunctionTypeImpl get nothingToVoidFunctionTypeNone { |
| return _functionType( |
| returns: voidType, |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| |
| FunctionTypeImpl get nothingToVoidFunctionTypeQuestion { |
| return _functionType( |
| returns: voidType, |
| nullabilitySuffix: NullabilitySuffix.question, |
| ); |
| } |
| |
| FunctionTypeImpl get nothingToVoidFunctionTypeStar { |
| return _functionType( |
| returns: voidType, |
| nullabilitySuffix: NullabilitySuffix.star, |
| ); |
| } |
| |
| DartType get objectClassTypeNone => (typeProvider.objectType as TypeImpl) |
| .withNullability(NullabilitySuffix.none); |
| |
| DartType get objectClassTypeQuestion => (typeProvider.objectType as TypeImpl) |
| .withNullability(NullabilitySuffix.question); |
| |
| DartType get objectClassTypeStar => (typeProvider.objectType as TypeImpl) |
| .withNullability(NullabilitySuffix.star); |
| |
| DartType get questionType => (typeProvider.stringType as TypeImpl) |
| .withNullability(NullabilitySuffix.question); |
| |
| DartType get starType => (typeProvider.stringType as TypeImpl) |
| .withNullability(NullabilitySuffix.star); |
| |
| InterfaceTypeImpl get stringClassTypeNone { |
| return _interfaceType( |
| typeProvider.stringType.element, |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| |
| InterfaceTypeImpl get stringClassTypeQuestion { |
| return _interfaceType( |
| typeProvider.stringType.element, |
| nullabilitySuffix: NullabilitySuffix.question, |
| ); |
| } |
| |
| InterfaceTypeImpl get stringClassTypeStar { |
| return _interfaceType( |
| typeProvider.stringType.element, |
| nullabilitySuffix: NullabilitySuffix.star, |
| ); |
| } |
| |
| InterfaceTypeImpl futureOrTypeNone({@required DartType argument}) { |
| var element = typeProvider.futureOrElement; |
| return _interfaceType( |
| element, |
| typeArguments: <DartType>[argument], |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| |
| InterfaceTypeImpl futureOrTypeQuestion({@required DartType argument}) { |
| var element = typeProvider.futureOrElement; |
| return _interfaceType( |
| element, |
| typeArguments: <DartType>[argument], |
| nullabilitySuffix: NullabilitySuffix.question, |
| ); |
| } |
| |
| InterfaceTypeImpl futureOrTypeStar({@required DartType argument}) { |
| var element = typeProvider.futureOrElement; |
| return _interfaceType( |
| element, |
| typeArguments: <DartType>[argument], |
| nullabilitySuffix: NullabilitySuffix.star, |
| ); |
| } |
| |
| InterfaceTypeImpl listClassTypeNone(DartType argument) { |
| var element = typeProvider.listElement; |
| return _interfaceType( |
| element, |
| typeArguments: <DartType>[argument], |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| |
| InterfaceTypeImpl listClassTypeQuestion(DartType argument) { |
| var element = typeProvider.listElement; |
| return _interfaceType( |
| element, |
| typeArguments: <DartType>[argument], |
| nullabilitySuffix: NullabilitySuffix.question, |
| ); |
| } |
| |
| InterfaceTypeImpl listClassTypeStar(DartType argument) { |
| var element = typeProvider.listElement; |
| return _interfaceType( |
| element, |
| typeArguments: <DartType>[argument], |
| nullabilitySuffix: NullabilitySuffix.star, |
| ); |
| } |
| |
| test_isNonNullable_dynamic() { |
| expect(typeSystem.isNonNullable(dynamicType), false); |
| } |
| |
| test_isNonNullable_function_none() { |
| expect(typeSystem.isNonNullable(nothingToVoidFunctionTypeNone), true); |
| } |
| |
| test_isNonNullable_function_question() { |
| expect(typeSystem.isNonNullable(nothingToVoidFunctionTypeQuestion), false); |
| } |
| |
| test_isNonNullable_function_star() { |
| expect(typeSystem.isNonNullable(nothingToVoidFunctionTypeStar), true); |
| } |
| |
| test_isNonNullable_functionClass_none() { |
| expect(typeSystem.isNonNullable(functionClassTypeNone), true); |
| } |
| |
| test_isNonNullable_functionClass_question() { |
| expect(typeSystem.isNonNullable(functionClassTypeQuestion), false); |
| } |
| |
| test_isNonNullable_functionClass_star() { |
| expect(typeSystem.isNonNullable(functionClassTypeStar), true); |
| } |
| |
| test_isNonNullable_futureOr_noneArgument_none() { |
| expect( |
| typeSystem.isNonNullable( |
| futureOrTypeNone(argument: noneType), |
| ), |
| true, |
| ); |
| } |
| |
| test_isNonNullable_futureOr_noneArgument_question() { |
| expect( |
| typeSystem.isNonNullable( |
| futureOrTypeQuestion(argument: noneType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isNonNullable_futureOr_noneArgument_star() { |
| expect( |
| typeSystem.isNonNullable( |
| futureOrTypeStar(argument: noneType), |
| ), |
| true, |
| ); |
| } |
| |
| test_isNonNullable_futureOr_questionArgument_none() { |
| expect( |
| typeSystem.isNonNullable( |
| futureOrTypeNone(argument: questionType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isNonNullable_futureOr_questionArgument_question() { |
| expect( |
| typeSystem.isNonNullable( |
| futureOrTypeQuestion(argument: questionType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isNonNullable_futureOr_questionArgument_star() { |
| expect( |
| typeSystem.isNonNullable( |
| futureOrTypeStar(argument: questionType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isNonNullable_futureOr_starArgument_none() { |
| expect( |
| typeSystem.isNonNullable( |
| futureOrTypeNone(argument: starType), |
| ), |
| true, |
| ); |
| } |
| |
| test_isNonNullable_futureOr_starArgument_question() { |
| expect( |
| typeSystem.isNonNullable( |
| futureOrTypeStar(argument: questionType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isNonNullable_futureOr_starArgument_star() { |
| expect( |
| typeSystem.isNonNullable( |
| futureOrTypeStar(argument: starType), |
| ), |
| true, |
| ); |
| } |
| |
| test_isNonNullable_interface_none() { |
| expect(typeSystem.isNonNullable(noneType), true); |
| } |
| |
| test_isNonNullable_interface_question() { |
| expect(typeSystem.isNonNullable(questionType), false); |
| } |
| |
| test_isNonNullable_interface_star() { |
| expect(typeSystem.isNonNullable(starType), true); |
| } |
| |
| test_isNonNullable_never() { |
| expect(typeSystem.isNonNullable(neverType), true); |
| } |
| |
| test_isNonNullable_null() { |
| expect(typeSystem.isNonNullable(nullType), false); |
| } |
| |
| test_isNonNullable_typeParameter_noneBound_none() { |
| expect( |
| typeSystem.isNonNullable( |
| typeParameterTypeNone(bound: noneType), |
| ), |
| true, |
| ); |
| } |
| |
| test_isNonNullable_typeParameter_noneBound_question() { |
| expect( |
| typeSystem.isNonNullable( |
| typeParameterTypeQuestion(bound: noneType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isNonNullable_typeParameter_questionBound_none() { |
| expect( |
| typeSystem.isNonNullable( |
| typeParameterTypeNone(bound: questionType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isNonNullable_typeParameter_questionBound_question() { |
| expect( |
| typeSystem.isNonNullable( |
| typeParameterTypeQuestion(bound: questionType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isNonNullable_typeParameter_starBound_star() { |
| expect( |
| typeSystem.isNonNullable( |
| typeParameterTypeStar(bound: starType), |
| ), |
| true, |
| ); |
| } |
| |
| test_isNonNullable_void() { |
| expect(typeSystem.isNonNullable(voidType), false); |
| } |
| |
| test_isNullable_dynamic() { |
| expect(typeSystem.isNullable(dynamicType), true); |
| } |
| |
| test_isNullable_function_none() { |
| expect(typeSystem.isNullable(nothingToVoidFunctionTypeNone), false); |
| } |
| |
| test_isNullable_function_question() { |
| expect(typeSystem.isNullable(nothingToVoidFunctionTypeQuestion), true); |
| } |
| |
| test_isNullable_function_star() { |
| expect(typeSystem.isNullable(nothingToVoidFunctionTypeStar), false); |
| } |
| |
| test_isNullable_functionClass_none() { |
| expect(typeSystem.isNullable(functionClassTypeNone), false); |
| } |
| |
| test_isNullable_functionClass_question() { |
| expect(typeSystem.isNullable(functionClassTypeQuestion), true); |
| } |
| |
| test_isNullable_functionClass_star() { |
| expect(typeSystem.isNullable(functionClassTypeStar), false); |
| } |
| |
| test_isNullable_futureOr_noneArgument_none() { |
| expect( |
| typeSystem.isNullable( |
| futureOrTypeNone(argument: noneType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isNullable_futureOr_noneArgument_question() { |
| expect( |
| typeSystem.isNullable( |
| futureOrTypeQuestion(argument: noneType), |
| ), |
| true, |
| ); |
| } |
| |
| test_isNullable_futureOr_noneArgument_star() { |
| expect( |
| typeSystem.isNullable( |
| futureOrTypeStar(argument: noneType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isNullable_futureOr_questionArgument_none() { |
| expect( |
| typeSystem.isNullable( |
| futureOrTypeNone(argument: questionType), |
| ), |
| true, |
| ); |
| } |
| |
| test_isNullable_futureOr_questionArgument_question() { |
| expect( |
| typeSystem.isNullable( |
| futureOrTypeQuestion(argument: questionType), |
| ), |
| true, |
| ); |
| } |
| |
| test_isNullable_futureOr_questionArgument_star() { |
| expect( |
| typeSystem.isNullable( |
| futureOrTypeStar(argument: questionType), |
| ), |
| true, |
| ); |
| } |
| |
| test_isNullable_futureOr_starArgument_none() { |
| expect( |
| typeSystem.isNullable( |
| futureOrTypeNone(argument: starType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isNullable_futureOr_starArgument_question() { |
| expect( |
| typeSystem.isNullable( |
| futureOrTypeQuestion(argument: starType), |
| ), |
| true, |
| ); |
| } |
| |
| test_isNullable_futureOr_starArgument_star() { |
| expect( |
| typeSystem.isNullable( |
| futureOrTypeStar(argument: starType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isNullable_interface_none() { |
| expect(typeSystem.isNullable(noneType), false); |
| } |
| |
| test_isNullable_interface_question() { |
| expect(typeSystem.isNullable(questionType), true); |
| } |
| |
| test_isNullable_interface_star() { |
| expect(typeSystem.isNullable(starType), false); |
| } |
| |
| test_isNullable_Never() { |
| expect(typeSystem.isNullable(neverType), false); |
| } |
| |
| test_isNullable_never() { |
| expect(typeSystem.isNullable(neverType), false); |
| } |
| |
| test_isNullable_null() { |
| expect(typeSystem.isNullable(nullType), true); |
| } |
| |
| test_isNullable_typeParameter_noneBound_none() { |
| expect( |
| typeSystem.isNullable( |
| typeParameterTypeNone(bound: noneType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isNullable_typeParameter_noneBound_question() { |
| expect( |
| typeSystem.isNullable( |
| typeParameterTypeQuestion(bound: noneType), |
| ), |
| true, |
| ); |
| } |
| |
| test_isNullable_typeParameter_questionBound_none() { |
| expect( |
| typeSystem.isNullable( |
| typeParameterTypeNone(bound: questionType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isNullable_typeParameter_questionBound_question() { |
| expect( |
| typeSystem.isNullable( |
| typeParameterTypeQuestion(bound: questionType), |
| ), |
| true, |
| ); |
| } |
| |
| test_isNullable_typeParameter_starBound_star() { |
| expect( |
| typeSystem.isNullable( |
| typeParameterTypeStar(bound: starType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isNullable_void() { |
| expect(typeSystem.isNullable(voidType), true); |
| } |
| |
| test_isPotentiallyNonNullable_dynamic() { |
| expect(typeSystem.isPotentiallyNonNullable(dynamicType), false); |
| } |
| |
| test_isPotentiallyNonNullable_futureOr_noneArgument_none() { |
| expect( |
| typeSystem.isPotentiallyNonNullable( |
| futureOrTypeNone(argument: noneType), |
| ), |
| true, |
| ); |
| } |
| |
| test_isPotentiallyNonNullable_futureOr_questionArgument_none() { |
| expect( |
| typeSystem.isPotentiallyNonNullable( |
| futureOrTypeNone(argument: questionType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isPotentiallyNonNullable_futureOr_starArgument_none() { |
| expect( |
| typeSystem.isPotentiallyNonNullable( |
| futureOrTypeNone(argument: starType), |
| ), |
| true, |
| ); |
| } |
| |
| test_isPotentiallyNonNullable_never() { |
| expect(typeSystem.isPotentiallyNonNullable(neverType), true); |
| } |
| |
| test_isPotentiallyNonNullable_none() { |
| expect(typeSystem.isPotentiallyNonNullable(noneType), true); |
| } |
| |
| test_isPotentiallyNonNullable_null() { |
| expect(typeSystem.isPotentiallyNonNullable(nullType), false); |
| } |
| |
| test_isPotentiallyNonNullable_question() { |
| expect(typeSystem.isPotentiallyNonNullable(questionType), false); |
| } |
| |
| test_isPotentiallyNonNullable_star() { |
| expect(typeSystem.isPotentiallyNonNullable(starType), true); |
| } |
| |
| test_isPotentiallyNonNullable_void() { |
| expect(typeSystem.isPotentiallyNonNullable(voidType), false); |
| } |
| |
| test_isPotentiallyNullable_dynamic() { |
| expect(typeSystem.isPotentiallyNullable(dynamicType), true); |
| } |
| |
| test_isPotentiallyNullable_futureOr_noneArgument_none() { |
| expect( |
| typeSystem.isPotentiallyNullable( |
| futureOrTypeNone(argument: noneType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isPotentiallyNullable_futureOr_questionArgument_none() { |
| expect( |
| typeSystem.isPotentiallyNullable( |
| futureOrTypeNone(argument: questionType), |
| ), |
| true, |
| ); |
| } |
| |
| test_isPotentiallyNullable_futureOr_starArgument_none() { |
| expect( |
| typeSystem.isPotentiallyNullable( |
| futureOrTypeNone(argument: starType), |
| ), |
| false, |
| ); |
| } |
| |
| test_isPotentiallyNullable_never() { |
| expect(typeSystem.isPotentiallyNullable(neverType), false); |
| } |
| |
| test_isPotentiallyNullable_none() { |
| expect(typeSystem.isPotentiallyNullable(noneType), false); |
| } |
| |
| test_isPotentiallyNullable_null() { |
| expect(typeSystem.isPotentiallyNullable(nullType), true); |
| } |
| |
| test_isPotentiallyNullable_question() { |
| expect(typeSystem.isPotentiallyNullable(questionType), true); |
| } |
| |
| test_isPotentiallyNullable_star() { |
| expect(typeSystem.isPotentiallyNullable(starType), false); |
| } |
| |
| test_isPotentiallyNullable_void() { |
| expect(typeSystem.isPotentiallyNullable(voidType), true); |
| } |
| |
| test_promoteToNonNull_dynamic() { |
| expect( |
| typeSystem.promoteToNonNull(dynamicType), |
| dynamicType, |
| ); |
| } |
| |
| test_promoteToNonNull_functionType() { |
| // NonNull(T0 Function(...)) = T0 Function(...) |
| expect( |
| typeSystem.promoteToNonNull(nothingToVoidFunctionTypeQuestion), |
| nothingToVoidFunctionTypeNone, |
| ); |
| } |
| |
| test_promoteToNonNull_futureOr_question() { |
| // NonNull(FutureOr<T>) = FutureOr<T> |
| expect( |
| typeSystem.promoteToNonNull( |
| futureOrTypeQuestion(argument: stringClassTypeQuestion), |
| ), |
| futureOrTypeNone(argument: stringClassTypeQuestion), |
| ); |
| } |
| |
| test_promoteToNonNull_interfaceType_function_none() { |
| expect( |
| typeSystem.promoteToNonNull(functionClassTypeQuestion), |
| functionClassTypeNone, |
| ); |
| } |
| |
| test_promoteToNonNull_interfaceType_none() { |
| expect( |
| typeSystem.promoteToNonNull(stringClassTypeNone), |
| stringClassTypeNone, |
| ); |
| } |
| |
| test_promoteToNonNull_interfaceType_question() { |
| expect( |
| typeSystem.promoteToNonNull(stringClassTypeQuestion), |
| stringClassTypeNone, |
| ); |
| } |
| |
| test_promoteToNonNull_interfaceType_question_withTypeArguments() { |
| // NonNull(C<T1, ... , Tn>) = C<T1, ... , Tn> |
| // NonNull(List<String?>?) = List<String?> |
| expect( |
| typeSystem.promoteToNonNull( |
| listClassTypeQuestion(stringClassTypeQuestion), |
| ), |
| listClassTypeNone(stringClassTypeQuestion), |
| ); |
| } |
| |
| test_promoteToNonNull_interfaceType_star() { |
| expect( |
| typeSystem.promoteToNonNull(stringClassTypeStar), |
| stringClassTypeNone, |
| ); |
| } |
| |
| test_promoteToNonNull_never() { |
| expect(typeSystem.promoteToNonNull(neverType), neverType); |
| } |
| |
| test_promoteToNonNull_null() { |
| expect(typeSystem.promoteToNonNull(nullType), neverType); |
| } |
| |
| test_promoteToNonNull_typeParameter_noneBound_none() { |
| expect( |
| typeSystem.promoteToNonNull( |
| typeParameterTypeNone(bound: noneType), |
| ), |
| typeParameterTypeNone(bound: noneType), |
| ); |
| } |
| |
| test_promoteToNonNull_typeParameter_nullBound_none() { |
| expect( |
| typeSystem.promoteToNonNull( |
| typeParameterTypeNone(bound: null), |
| ), |
| typeParameterTypeNone(bound: objectClassTypeNone), |
| ); |
| } |
| |
| test_promoteToNonNull_typeParameter_questionBound_none() { |
| expect( |
| typeSystem.promoteToNonNull( |
| typeParameterTypeNone(bound: stringClassTypeQuestion), |
| ), |
| typeParameterTypeNone(bound: stringClassTypeNone), |
| ); |
| } |
| |
| test_promoteToNonNull_typeParameter_questionBound_question() { |
| expect( |
| typeSystem.promoteToNonNull( |
| typeParameterTypeQuestion(bound: stringClassTypeQuestion), |
| ), |
| typeParameterTypeNone(bound: stringClassTypeNone), |
| ); |
| } |
| |
| test_promoteToNonNull_typeParameter_questionBound_star() { |
| expect( |
| typeSystem.promoteToNonNull( |
| typeParameterTypeStar(bound: stringClassTypeQuestion), |
| ), |
| typeParameterTypeNone(bound: stringClassTypeNone), |
| ); |
| } |
| |
| test_promoteToNonNull_typeParameter_starBound_none() { |
| expect( |
| typeSystem.promoteToNonNull( |
| typeParameterTypeNone(bound: stringClassTypeStar), |
| ), |
| typeParameterTypeNone(bound: stringClassTypeNone), |
| ); |
| } |
| |
| test_promoteToNonNull_void() { |
| expect( |
| typeSystem.promoteToNonNull(voidType), |
| voidType, |
| ); |
| } |
| |
| DartType typeParameterTypeNone({@required DartType bound}) { |
| var element = _typeParameter('T', bound: bound); |
| return _typeParameterType( |
| element, |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| |
| DartType typeParameterTypeQuestion({@required DartType bound}) { |
| var element = _typeParameter('T', bound: bound); |
| return _typeParameterType( |
| element, |
| nullabilitySuffix: NullabilitySuffix.question, |
| ); |
| } |
| |
| DartType typeParameterTypeStar({@required DartType bound}) { |
| var element = _typeParameter('T', bound: bound); |
| return _typeParameterType( |
| element, |
| nullabilitySuffix: NullabilitySuffix.star, |
| ); |
| } |
| } |