| // 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/member.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| import 'package:analyzer/src/dart/resolver/variance.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: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 'elements_types_mixin.dart'; |
| import 'test_analysis_context.dart'; |
| |
| main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(AssignabilityTest); |
| defineReflectiveTests(ConstraintMatchingTest); |
| defineReflectiveTests(GenericFunctionInferenceTest); |
| defineReflectiveTests(GreatestLowerBoundTest); |
| defineReflectiveTests(LeastUpperBoundFunctionsTest); |
| defineReflectiveTests(LeastUpperBoundTest); |
| defineReflectiveTests(TypeSystemTest); |
| }); |
| } |
| |
| abstract class AbstractTypeSystemTest with ElementsTypesMixin { |
| TypeProvider typeProvider; |
| Dart2TypeSystem typeSystem; |
| |
| InterfaceType get doubleType => typeProvider.doubleType; |
| |
| InterfaceType get intType => typeProvider.intType; |
| |
| 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; |
| |
| typeProvider = typeProvider; |
| typeSystem = typeSystem; |
| } |
| } |
| |
| @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), |
| neverStar, |
| ]; |
| |
| _checkGroups(neverStar, interassignable: interassignable); |
| } |
| |
| void test_isAssignableTo_call_method() { |
| var B = class_( |
| name: 'B', |
| methods: [ |
| method('call', objectType, parameters: [ |
| requiredParameter(name: '_', type: intType), |
| ]), |
| ], |
| ); |
| |
| _checkIsStrictAssignableTo( |
| interfaceType(B), |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: 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, |
| neverStar, |
| ]; |
| 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), |
| neverStar, |
| ]; |
| _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: [ |
| typeParameterTypeStar(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, |
| neverStar, |
| ]; |
| List<DartType> unrelated = <DartType>[ |
| doubleType, |
| stringType, |
| interfaceType(A), |
| ]; |
| |
| _checkGroups(intType, |
| interassignable: interassignable, unrelated: unrelated); |
| } |
| |
| void test_isAssignableTo_named_optional() { |
| var r = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: intType, |
| ); |
| var o = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: intType), |
| ], |
| returnType: intType, |
| ); |
| var n = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'x', type: intType), |
| ], |
| returnType: intType, |
| ); |
| |
| var rr = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| requiredParameter(type: intType), |
| ], |
| returnType: intType, |
| ); |
| var ro = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| positionalParameter(type: intType), |
| ], |
| returnType: intType, |
| ); |
| var rn = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| namedParameter(name: 'x', type: intType), |
| ], |
| returnType: intType, |
| ); |
| var oo = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: intType), |
| positionalParameter(type: intType), |
| ], |
| returnType: intType, |
| ); |
| var nn = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'x', type: intType), |
| namedParameter(name: 'y', type: intType), |
| ], |
| returnType: intType, |
| ); |
| var nnn = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'x', type: intType), |
| namedParameter(name: 'y', type: intType), |
| namedParameter(name: 'z', type: intType), |
| ], |
| returnType: 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, |
| neverStar, |
| ]; |
| List<DartType> unrelated = <DartType>[ |
| stringType, |
| interfaceType(A), |
| ]; |
| |
| _checkGroups(numType, |
| interassignable: interassignable, unrelated: unrelated); |
| } |
| |
| void test_isAssignableTo_simple_function() { |
| var top = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: objectType, |
| ); |
| |
| var left = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: intType, |
| ); |
| |
| var right = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: objectType), |
| ], |
| returnType: objectType, |
| ); |
| |
| var bottom = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: objectType), |
| ], |
| returnType: intType, |
| ); |
| |
| _checkCrossLattice(top, left, right, bottom); |
| } |
| |
| void test_isAssignableTo_void_functions() { |
| var top = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| |
| var bottom = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: objectType), |
| ], |
| returnType: 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. |
| glb = typeSystem.getGreatestLowerBound(type2, type1); |
| 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. |
| lub = typeSystem.getLeastUpperBound(type2, type1); |
| expect(lub, expectedResult); |
| } |
| } |
| |
| @reflectiveTest |
| class ConstraintMatchingTest extends AbstractTypeSystemTest { |
| TypeParameterType T; |
| |
| void setUp() { |
| super.setUp(); |
| T = typeParameterTypeStar( |
| typeParameter('T'), |
| ); |
| } |
| |
| void test_function_coreFunction() { |
| _checkOrdinarySubtypeMatch( |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: stringType, |
| ), |
| typeProvider.functionType, |
| [T], |
| covariant: true, |
| ); |
| } |
| |
| void test_function_parameter_types() { |
| _checkIsSubtypeMatchOf( |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: T), |
| ], |
| returnType: intType, |
| ), |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| ], |
| returnType: intType, |
| ), |
| [T], |
| ['String <: T'], |
| covariant: true, |
| ); |
| } |
| |
| void test_function_return_types() { |
| _checkIsSubtypeMatchOf( |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: T, |
| ), |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: 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, |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: 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 = typeParameterTypeStar(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 = typeParameterTypeStar(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 = typeParameterTypeStar(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 = typeParameterTypeStar(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( |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: 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( |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: 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( |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: 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(typeParameterTypeStar(tFrom))); |
| var cast = functionTypeStar( |
| typeFormals: [tFrom, tTo], |
| parameters: [ |
| requiredParameter( |
| type: typeParameterTypeStar(tFrom), |
| ), |
| ], |
| returnType: typeParameterTypeStar(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: typeParameterTypeStar(CT)); |
| var m = method( |
| 'm', |
| typeParameterTypeStar(S), |
| typeFormals: [S], |
| parameters: [ |
| requiredParameter( |
| name: '_', |
| type: typeParameterTypeStar(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(typeParameterTypeStar(CT)); |
| var S = typeParameter('S', bound: iterableOfT); |
| var m = method( |
| 'm', |
| typeParameterTypeStar(S), |
| typeFormals: [S], |
| parameters: [ |
| requiredParameter( |
| name: '_', |
| type: typeParameterTypeStar(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: [typeParameterTypeStar(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 = typeParameterTypeStar(S); |
| S.bound = interfaceType(A, typeArguments: [typeS]); |
| |
| // (S, S) -> S |
| var clone = functionTypeStar( |
| typeFormals: [S], |
| parameters: [ |
| requiredParameter(type: typeS), |
| requiredParameter(type: typeS), |
| ], |
| returnType: 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 = functionTypeStar( |
| typeFormals: [tFrom, tTo], |
| parameters: [ |
| requiredParameter( |
| type: typeParameterTypeStar(tFrom), |
| ), |
| ], |
| returnType: typeParameterTypeStar(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: typeParameterTypeStar(tFrom), |
| ); |
| var cast = functionTypeStar( |
| typeFormals: [tFrom, tTo], |
| parameters: [ |
| requiredParameter( |
| type: typeParameterTypeStar(tFrom), |
| ), |
| ], |
| returnType: typeParameterTypeStar(tTo), |
| ); |
| expect(_inferCall(cast, [intType]), [intType, intType]); |
| } |
| |
| void test_parametersToFunctionParam() { |
| // <T>(f(T t)) -> T |
| var T = typeParameter('T'); |
| var cast = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: typeParameterTypeStar(T), |
| ), |
| ], |
| returnType: dynamicType, |
| ), |
| ), |
| ], |
| returnType: typeParameterTypeStar(T), |
| ); |
| expect( |
| _inferCall(cast, [ |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: numType), |
| ], |
| returnType: dynamicType, |
| ) |
| ]), |
| [numType], |
| ); |
| } |
| |
| void test_parametersUseLeastUpperBound() { |
| // <T>(T x, T y) -> T |
| var T = typeParameter('T'); |
| var cast = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: typeParameterTypeStar(T), |
| ); |
| expect(_inferCall(cast, [intType, doubleType]), [numType]); |
| } |
| |
| void test_parameterTypeUsesUpperBound() { |
| // <T extends num>(T) -> dynamic |
| var T = typeParameter('T', bound: numType); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: dynamicType, |
| ); |
| expect(_inferCall(f, [intType]), [intType]); |
| } |
| |
| void test_returnFunctionWithGenericParameter() { |
| // <T>(T -> T) -> (T -> void) |
| var T = typeParameter('T'); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: typeParameterTypeStar(T), |
| ), |
| ), |
| ], |
| returnType: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: voidType, |
| ), |
| ); |
| expect( |
| _inferCall(f, [ |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: numType), |
| ], |
| returnType: intType, |
| ), |
| ]), |
| [intType], |
| ); |
| } |
| |
| void test_returnFunctionWithGenericParameterAndContext() { |
| // <T>(T -> T) -> (T -> Null) |
| var T = typeParameter('T'); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: typeParameterTypeStar(T), |
| ), |
| ), |
| ], |
| returnType: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: nullType, |
| ), |
| ); |
| expect( |
| _inferCall( |
| f, |
| [], |
| returnType: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: numType), |
| ], |
| returnType: intType, |
| ), |
| ), |
| [numType], |
| ); |
| } |
| |
| void test_returnFunctionWithGenericParameterAndReturn() { |
| // <T>(T -> T) -> (T -> T) |
| var T = typeParameter('T'); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: typeParameterTypeStar(T), |
| ), |
| ), |
| ], |
| returnType: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: typeParameterTypeStar(T), |
| ), |
| ); |
| expect( |
| _inferCall(f, [ |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: numType), |
| ], |
| returnType: intType, |
| ) |
| ]), |
| [intType], |
| ); |
| } |
| |
| void test_returnFunctionWithGenericReturn() { |
| // <T>(T -> T) -> (() -> T) |
| var T = typeParameter('T'); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: typeParameterTypeStar(T), |
| ), |
| ), |
| ], |
| returnType: functionTypeStar( |
| returnType: typeParameterTypeStar(T), |
| ), |
| ); |
| expect( |
| _inferCall(f, [ |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: numType), |
| ], |
| returnType: intType, |
| ) |
| ]), |
| [intType], |
| ); |
| } |
| |
| void test_returnTypeFromContext() { |
| // <T>() -> T |
| var T = typeParameter('T'); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| returnType: typeParameterTypeStar(T), |
| ); |
| expect(_inferCall(f, [], returnType: stringType), [stringType]); |
| } |
| |
| void test_returnTypeWithBoundFromContext() { |
| // <T extends num>() -> T |
| var T = typeParameter('T', bound: numType); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| returnType: typeParameterTypeStar(T), |
| ); |
| expect(_inferCall(f, [], returnType: doubleType), [doubleType]); |
| } |
| |
| void test_returnTypeWithBoundFromInvalidContext() { |
| // <T extends num>() -> T |
| var T = typeParameter('T', bound: numType); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| returnType: typeParameterTypeStar(T), |
| ); |
| expect(_inferCall(f, [], returnType: stringType), [nullType]); |
| } |
| |
| void test_unifyParametersToFunctionParam() { |
| // <T>(f(T t), g(T t)) -> T |
| var T = typeParameter('T'); |
| var cast = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: typeParameterTypeStar(T), |
| ), |
| ], |
| returnType: dynamicType, |
| ), |
| ), |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: typeParameterTypeStar(T), |
| ), |
| ], |
| returnType: dynamicType, |
| ), |
| ), |
| ], |
| returnType: typeParameterTypeStar(T), |
| ); |
| expect( |
| _inferCall(cast, [ |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: dynamicType, |
| ), |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: doubleType), |
| ], |
| returnType: dynamicType, |
| ) |
| ]), |
| [nullType], |
| ); |
| } |
| |
| void test_unusedReturnTypeIsDynamic() { |
| // <T>() -> T |
| var T = typeParameter('T'); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| returnType: typeParameterTypeStar(T), |
| ); |
| expect(_inferCall(f, []), [dynamicType]); |
| } |
| |
| void test_unusedReturnTypeWithUpperBound() { |
| // <T extends num>() -> T |
| var T = typeParameter('T', bound: numType); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| returnType: typeParameterTypeStar(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( |
| neverStar, functionTypeStar(returnType: voidType), neverStar); |
| } |
| |
| void test_bottom_interface() { |
| var A = class_(name: 'A'); |
| _checkGreatestLowerBound(neverStar, interfaceType(A), neverStar); |
| } |
| |
| void test_bottom_typeParam() { |
| var T = typeParameter('T'); |
| _checkGreatestLowerBound(neverStar, typeParameterTypeStar(T), neverStar); |
| } |
| |
| 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, neverStar, neverStar); |
| } |
| |
| void test_dynamic_function() { |
| _checkGreatestLowerBound( |
| dynamicType, |
| functionTypeStar(returnType: voidType), |
| functionTypeStar(returnType: voidType)); |
| } |
| |
| 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 = typeParameterTypeStar(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 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: intType), |
| namedParameter(name: 'b', type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'b', type: doubleType), |
| namedParameter(name: 'c', type: stringType), |
| ], |
| returnType: voidType, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: intType), |
| namedParameter(name: 'b', type: numType), |
| namedParameter(name: 'c', type: stringType), |
| ], |
| returnType: voidType, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsDifferentOptionalArityTakeMax() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: doubleType), |
| positionalParameter(type: stringType), |
| positionalParameter(type: objectType), |
| ], |
| returnType: voidType, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: numType), |
| positionalParameter(type: stringType), |
| positionalParameter(type: objectType), |
| ], |
| returnType: voidType, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsDifferentRequiredArityBecomeOptional() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| requiredParameter(type: intType), |
| requiredParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| positionalParameter(type: intType), |
| positionalParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsFromDynamic() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: dynamicType), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: dynamicType), |
| ], |
| returnType: voidType, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsGlbReturnType() { |
| var type1 = functionTypeStar( |
| returnType: intType, |
| ); |
| var type2 = functionTypeStar( |
| returnType: numType, |
| ); |
| var expected = functionTypeStar( |
| returnType: intType, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsLubNamedParams() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: stringType), |
| namedParameter(name: 'b', type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: intType), |
| namedParameter(name: 'b', type: numType), |
| ], |
| returnType: voidType, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: objectType), |
| namedParameter(name: 'b', type: numType), |
| ], |
| returnType: voidType, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsLubPositionalParams() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: stringType), |
| positionalParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: intType), |
| positionalParameter(type: numType), |
| ], |
| returnType: voidType, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: objectType), |
| positionalParameter(type: numType), |
| ], |
| returnType: voidType, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsLubRequiredParams() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| requiredParameter(type: intType), |
| requiredParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| requiredParameter(type: doubleType), |
| requiredParameter(type: numType), |
| ], |
| returnType: voidType, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: objectType), |
| requiredParameter(type: numType), |
| requiredParameter(type: numType), |
| ], |
| returnType: voidType, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsMixedOptionalAndRequiredBecomeOptional() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| requiredParameter(type: intType), |
| positionalParameter(type: intType), |
| positionalParameter(type: intType), |
| positionalParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| positionalParameter(type: intType), |
| positionalParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| positionalParameter(type: intType), |
| positionalParameter(type: intType), |
| positionalParameter(type: intType), |
| positionalParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| _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 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| namedParameter(name: 'a', type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: intType), |
| ], |
| returnType: voidType, |
| ); |
| _checkGreatestLowerBound(type1, type2, neverStar); |
| } |
| |
| void test_functionsSameType_withNamed() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| requiredParameter(type: intType), |
| requiredParameter(type: numType), |
| namedParameter(name: 'n', type: numType), |
| ], |
| returnType: intType, |
| ); |
| |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| requiredParameter(type: intType), |
| requiredParameter(type: numType), |
| namedParameter(name: 'n', type: numType), |
| ], |
| returnType: intType, |
| ); |
| |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| requiredParameter(type: intType), |
| requiredParameter(type: numType), |
| namedParameter(name: 'n', type: numType), |
| ], |
| returnType: intType, |
| ); |
| |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsSameType_withOptional() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| requiredParameter(type: intType), |
| requiredParameter(type: numType), |
| positionalParameter(type: doubleType), |
| ], |
| returnType: intType, |
| ); |
| |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| requiredParameter(type: intType), |
| requiredParameter(type: numType), |
| positionalParameter(type: doubleType), |
| ], |
| returnType: intType, |
| ); |
| |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| requiredParameter(type: intType), |
| requiredParameter(type: numType), |
| positionalParameter(type: doubleType), |
| ], |
| returnType: intType, |
| ); |
| |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_interface_function() { |
| var A = class_(name: 'A'); |
| var typeA = interfaceType(A); |
| _checkGreatestLowerBound( |
| typeA, |
| functionTypeStar(returnType: voidType), |
| neverStar, |
| ); |
| } |
| |
| 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, |
| neverStar, |
| typeParameterTypeStar(T), |
| interfaceType(A), |
| functionTypeStar(returnType: voidType), |
| ]; |
| |
| for (DartType type in types) { |
| _checkGreatestLowerBound(type, type, type); |
| } |
| } |
| |
| void test_typeParam_function_noBound() { |
| var T = typeParameter('T'); |
| _checkGreatestLowerBound( |
| typeParameterTypeStar(T), |
| functionTypeStar(returnType: voidType), |
| neverStar, |
| ); |
| } |
| |
| 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(typeParameterTypeStar(T), typeC, neverStar); |
| } |
| |
| void test_typeParam_interface_noBound() { |
| // GLB(T, A) = ⊥ |
| var T = typeParameter('T'); |
| var A = class_(name: 'A'); |
| _checkGreatestLowerBound( |
| typeParameterTypeStar(T), |
| interfaceType(A), |
| neverStar, |
| ); |
| } |
| |
| 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, neverStar); |
| } |
| |
| void test_typeParameters_same() { |
| // GLB(List<int>, List<int>) = List<int> |
| var listOfIntType = listType(intType); |
| _checkGreatestLowerBound(listOfIntType, listOfIntType, listOfIntType); |
| } |
| |
| void test_typeParameters_covariant_same() { |
| // class A<out T> |
| var T = typeParameter('T', variance: Variance.covariant); |
| var A = class_(name: 'A', typeParameters: [T]); |
| |
| // A<num> |
| var aNum = interfaceType(A, typeArguments: [numType]); |
| |
| _checkLeastUpperBound(aNum, aNum, aNum); |
| } |
| |
| void test_typeParameters_covariant_different() { |
| // class A<out T> |
| var T = typeParameter('T', variance: Variance.covariant); |
| var A = class_(name: 'A', typeParameters: [T]); |
| |
| // A<num> |
| // A<int> |
| var aNum = interfaceType(A, typeArguments: [numType]); |
| var aInt = interfaceType(A, typeArguments: [intType]); |
| |
| _checkLeastUpperBound(aInt, aNum, aNum); |
| } |
| |
| void test_typeParameters_contravariant_same() { |
| // class A<in T> |
| var T = typeParameter('T', variance: Variance.contravariant); |
| var A = class_(name: 'A', typeParameters: [T]); |
| |
| // A<num> |
| var aNum = interfaceType(A, typeArguments: [numType]); |
| |
| _checkLeastUpperBound(aNum, aNum, aNum); |
| } |
| |
| void test_typeParameters_contravariant_different() { |
| // class A<in T> |
| var T = typeParameter('T', variance: Variance.contravariant); |
| var A = class_(name: 'A', typeParameters: [T]); |
| |
| // A<num> |
| // A<int> |
| var aNum = interfaceType(A, typeArguments: [numType]); |
| var aInt = interfaceType(A, typeArguments: [intType]); |
| |
| _checkLeastUpperBound(aInt, aNum, aInt); |
| } |
| |
| void test_typeParameters_invariant_same() { |
| // class A<inout T> |
| var T = typeParameter('T', variance: Variance.invariant); |
| var A = class_(name: 'A', typeParameters: [T]); |
| |
| // A<num> |
| var aNum = interfaceType(A, typeArguments: [numType]); |
| |
| _checkLeastUpperBound(aNum, aNum, aNum); |
| } |
| |
| void test_typeParameters_invariant_object() { |
| // class A<inout T> |
| var T = typeParameter('T', variance: Variance.invariant); |
| var A = class_(name: 'A', typeParameters: [T]); |
| |
| // A<num> |
| // A<int> |
| var aNum = interfaceType(A, typeArguments: [numType]); |
| var aInt = interfaceType(A, typeArguments: [intType]); |
| |
| _checkLeastUpperBound(aNum, aInt, objectType); |
| } |
| |
| void test_typeParameters_multi_basic() { |
| // class Multi<out T, inout U, in V> |
| var T = typeParameter('T', variance: Variance.covariant); |
| var U = typeParameter('T', variance: Variance.invariant); |
| var V = typeParameter('T', variance: Variance.contravariant); |
| var Multi = class_(name: 'A', typeParameters: [T, U, V]); |
| |
| // Multi<num, num, num> |
| // Multi<int, num, int> |
| var multiNumNumNum = |
| interfaceType(Multi, typeArguments: [numType, numType, numType]); |
| var multiIntNumInt = |
| interfaceType(Multi, typeArguments: [intType, numType, intType]); |
| |
| // We expect Multi<num, num, int> |
| var multiNumNumInt = |
| interfaceType(Multi, typeArguments: [numType, numType, intType]); |
| |
| _checkLeastUpperBound(multiNumNumNum, multiIntNumInt, multiNumNumInt); |
| } |
| |
| void test_typeParameters_multi_objectInterface() { |
| // class Multi<out T, inout U, in V> |
| var T = typeParameter('T', variance: Variance.covariant); |
| var U = typeParameter('T', variance: Variance.invariant); |
| var V = typeParameter('T', variance: Variance.contravariant); |
| var Multi = class_(name: 'A', typeParameters: [T, U, V]); |
| |
| // Multi<num, String, num> |
| // Multi<int, num, int> |
| var multiNumStringNum = |
| interfaceType(Multi, typeArguments: [numType, stringType, numType]); |
| var multiIntNumInt = |
| interfaceType(Multi, typeArguments: [intType, numType, intType]); |
| |
| _checkLeastUpperBound(multiNumStringNum, multiIntNumInt, objectType); |
| } |
| |
| void test_typeParameters_multi_objectType() { |
| // class Multi<out T, inout U, in V> |
| var T = typeParameter('T', variance: Variance.covariant); |
| var U = typeParameter('T', variance: Variance.invariant); |
| var V = typeParameter('T', variance: Variance.contravariant); |
| var Multi = class_(name: 'A', typeParameters: [T, U, V]); |
| |
| // Multi<String, num, num> |
| // Multi<int, num, int> |
| var multiStringNumNum = |
| interfaceType(Multi, typeArguments: [stringType, numType, numType]); |
| var multiIntNumInt = |
| interfaceType(Multi, typeArguments: [intType, numType, intType]); |
| |
| // We expect Multi<Object, num, int> |
| var multiObjectNumInt = |
| interfaceType(Multi, typeArguments: [objectType, numType, intType]); |
| |
| _checkLeastUpperBound(multiStringNumNum, multiIntNumInt, multiObjectNumInt); |
| } |
| |
| void test_unrelatedClasses() { |
| // class A |
| // class B |
| // class C |
| var A = class_(name: 'A'); |
| var B = class_(name: 'B'); |
| _checkGreatestLowerBound(interfaceType(A), interfaceType(B), neverStar); |
| } |
| |
| void test_void() { |
| var A = class_(name: 'A'); |
| var T = typeParameter('T'); |
| List<DartType> types = [ |
| neverStar, |
| functionTypeStar(returnType: voidType), |
| interfaceType(A), |
| typeParameterTypeStar(T), |
| ]; |
| for (DartType type in types) { |
| _checkGreatestLowerBound( |
| functionTypeStar(returnType: voidType), |
| functionTypeStar(returnType: type), |
| functionTypeStar(returnType: type), |
| ); |
| } |
| } |
| } |
| |
| @reflectiveTest |
| class LeastUpperBoundFunctionsTest extends BoundTestBase { |
| void test_differentRequiredArity() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| requiredParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| requiredParameter(type: intType), |
| requiredParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| _checkLeastUpperBound(type1, type2, typeProvider.functionType); |
| } |
| |
| void test_fuzzyArrows() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: dynamicType), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_glbNamedParams() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: stringType), |
| namedParameter(name: 'b', type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: intType), |
| namedParameter(name: 'b', type: numType), |
| ], |
| returnType: voidType, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: neverStar), |
| namedParameter(name: 'b', type: intType), |
| ], |
| returnType: voidType, |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_glbPositionalParams() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: stringType), |
| positionalParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: intType), |
| positionalParameter(type: numType), |
| ], |
| returnType: voidType, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: neverStar), |
| positionalParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_glbRequiredParams() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| requiredParameter(type: intType), |
| requiredParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| requiredParameter(type: doubleType), |
| requiredParameter(type: numType), |
| ], |
| returnType: voidType, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: neverStar), |
| requiredParameter(type: neverStar), |
| requiredParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_ignoreExtraNamedParams() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: intType), |
| namedParameter(name: 'b', type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: intType), |
| namedParameter(name: 'c', type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: intType), |
| ], |
| returnType: voidType, |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_ignoreExtraPositionalParams() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: intType), |
| positionalParameter(type: intType), |
| positionalParameter(type: stringType), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: intType), |
| ], |
| returnType: voidType, |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_lubReturnType() { |
| var type1 = functionTypeStar(returnType: intType); |
| var type2 = functionTypeStar(returnType: doubleType); |
| var expected = functionTypeStar(returnType: numType); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_sameType_withNamed() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| requiredParameter(type: intType), |
| requiredParameter(type: numType), |
| namedParameter(name: 'n', type: numType), |
| ], |
| returnType: intType, |
| ); |
| |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| requiredParameter(type: intType), |
| requiredParameter(type: numType), |
| namedParameter(name: 'n', type: numType), |
| ], |
| returnType: intType, |
| ); |
| |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| requiredParameter(type: intType), |
| requiredParameter(type: numType), |
| namedParameter(name: 'n', type: numType), |
| ], |
| returnType: intType, |
| ); |
| |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_sameType_withOptional() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| requiredParameter(type: intType), |
| requiredParameter(type: numType), |
| positionalParameter(type: doubleType), |
| ], |
| returnType: intType, |
| ); |
| |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| requiredParameter(type: intType), |
| requiredParameter(type: numType), |
| positionalParameter(type: doubleType), |
| ], |
| returnType: intType, |
| ); |
| |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| requiredParameter(type: intType), |
| requiredParameter(type: numType), |
| positionalParameter(type: doubleType), |
| ], |
| returnType: intType, |
| ); |
| |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_typeFormals_differentBounds() { |
| var T1 = typeParameter('T1', bound: intType); |
| var type1 = functionTypeStar( |
| typeFormals: [T1], |
| returnType: typeParameterTypeStar(T1), |
| ); |
| |
| var T2 = typeParameter('T2', bound: doubleType); |
| var type2 = functionTypeStar( |
| typeFormals: [T2], |
| returnType: typeParameterTypeStar(T2), |
| ); |
| |
| _checkLeastUpperBound(type1, type2, typeProvider.functionType); |
| } |
| |
| void test_typeFormals_differentNumber() { |
| var T1 = typeParameter('T1', bound: numType); |
| var type1 = functionTypeStar( |
| typeFormals: [T1], |
| returnType: typeParameterTypeStar(T1), |
| ); |
| |
| var type2 = functionTypeStar(returnType: intType); |
| |
| _checkLeastUpperBound(type1, type2, typeProvider.functionType); |
| } |
| |
| void test_typeFormals_sameBounds() { |
| var T1 = typeParameter('T1', bound: numType); |
| var type1 = functionTypeStar( |
| typeFormals: [T1], |
| returnType: typeParameterTypeStar(T1), |
| ); |
| |
| var T2 = typeParameter('T2', bound: numType); |
| var type2 = functionTypeStar( |
| typeFormals: [T2], |
| returnType: typeParameterTypeStar(T2), |
| ); |
| |
| var TE = typeParameter('T', bound: numType); |
| var expected = functionTypeStar( |
| typeFormals: [TE], |
| returnType: typeParameterTypeStar(TE), |
| ); |
| |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| } |
| |
| @reflectiveTest |
| class LeastUpperBoundTest extends BoundTestBase { |
| void test_bottom_function() { |
| _checkLeastUpperBound(neverStar, functionTypeStar(returnType: voidType), |
| functionTypeStar(returnType: voidType)); |
| } |
| |
| void test_bottom_interface() { |
| var A = class_(name: 'A'); |
| var typeA = interfaceType(A); |
| _checkLeastUpperBound(neverStar, typeA, typeA); |
| } |
| |
| void test_bottom_typeParam() { |
| var T = typeParameter('T'); |
| var typeT = typeParameterTypeStar(T); |
| _checkLeastUpperBound(neverStar, 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, neverStar, dynamicType); |
| } |
| |
| void test_dynamic_function() { |
| _checkLeastUpperBound( |
| dynamicType, functionTypeStar(returnType: voidType), 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, typeParameterTypeStar(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), functionTypeStar(returnType: voidType), 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 = functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| requiredParameter(type: intType), |
| requiredParameter(type: intType), |
| ], |
| returnType: voidType, |
| ), |
| ), |
| ], |
| returnType: voidType, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| requiredParameter(type: doubleType), |
| requiredParameter(type: numType), |
| ], |
| returnType: voidType, |
| ), |
| ), |
| ], |
| returnType: voidType, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: objectType), |
| requiredParameter(type: numType), |
| requiredParameter(type: numType), |
| ], |
| returnType: voidType, |
| ), |
| ), |
| ], |
| returnType: voidType, |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_nestedNestedFunctionsGlbInnermostParamTypes() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringType), |
| requiredParameter(type: intType), |
| requiredParameter(type: intType) |
| ], |
| returnType: voidType, |
| ), |
| ), |
| ], |
| returnType: voidType, |
| ), |
| ), |
| ], |
| returnType: voidType, |
| ); |
| expect( |
| type1.toString(withNullability: true), |
| 'void Function(void Function(void Function(String*, int*, int*)*)*)*', |
| ); |
| |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intType), |
| requiredParameter(type: doubleType), |
| requiredParameter(type: numType) |
| ], |
| returnType: voidType, |
| ), |
| ), |
| ], |
| returnType: voidType, |
| ), |
| ), |
| ], |
| returnType: voidType, |
| ); |
| expect( |
| type2.toString(withNullability: true), |
| 'void Function(void Function(void Function(int*, double*, num*)*)*)*', |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: neverStar), |
| requiredParameter(type: neverStar), |
| requiredParameter(type: intType) |
| ], |
| returnType: voidType, |
| ), |
| ), |
| ], |
| returnType: voidType, |
| ), |
| ), |
| ], |
| returnType: voidType, |
| ); |
| expect( |
| expected.toString(withNullability: true), |
| 'void Function(void Function(void Function(Never*, Never*, int*)*)*)*', |
| ); |
| |
| _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, |
| neverStar, |
| typeParameterTypeStar(T), |
| interfaceType(A), |
| functionTypeStar(returnType: voidType) |
| ]; |
| |
| 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 = typeParameterTypeStar(S); |
| |
| var T = typeParameter('T', bound: typeS); |
| var typeT = typeParameterTypeStar(T); |
| |
| _checkLeastUpperBound(typeT, typeS, typeS); |
| } |
| |
| void test_typeParam_class_implements_Function_ignored() { |
| var A = class_(name: 'A', superType: typeProvider.functionType); |
| var T = typeParameter('T', bound: interfaceType(A)); |
| _checkLeastUpperBound(typeParameterTypeStar(T), |
| functionTypeStar(returnType: voidType), objectType); |
| } |
| |
| void test_typeParam_fBounded() { |
| var T = typeParameter('Q'); |
| var A = class_(name: 'A', typeParameters: [T]); |
| |
| var S = typeParameter('S'); |
| var typeS = typeParameterTypeStar(S); |
| S.bound = interfaceType(A, typeArguments: [typeS]); |
| |
| var U = typeParameter('U'); |
| var typeU = typeParameterTypeStar(U); |
| U.bound = interfaceType(A, typeArguments: [typeU]); |
| |
| _checkLeastUpperBound( |
| typeS, |
| typeParameterTypeStar(U), |
| interfaceType(A, typeArguments: [objectType]), |
| ); |
| } |
| |
| void test_typeParam_function_bounded() { |
| var T = typeParameter('T', bound: typeProvider.functionType); |
| _checkLeastUpperBound( |
| typeParameterTypeStar(T), |
| functionTypeStar(returnType: voidType), |
| typeProvider.functionType, |
| ); |
| } |
| |
| void test_typeParam_function_noBound() { |
| var T = typeParameter('T'); |
| _checkLeastUpperBound( |
| typeParameterTypeStar(T), |
| functionTypeStar(returnType: voidType), |
| 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 = typeParameterTypeStar(T); |
| |
| _checkLeastUpperBound(typeT, typeC, typeA); |
| } |
| |
| void test_typeParam_interface_noBound() { |
| var T = typeParameter('T'); |
| var A = class_(name: 'A'); |
| _checkLeastUpperBound( |
| typeParameterTypeStar(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() { |
| var T = typeParameter('T'); |
| var A = class_(name: 'A'); |
| List<DartType> types = [ |
| neverStar, |
| functionTypeStar(returnType: voidType), |
| interfaceType(A), |
| typeParameterTypeStar(T), |
| ]; |
| for (DartType type in types) { |
| _checkLeastUpperBound( |
| functionTypeStar(returnType: voidType), |
| functionTypeStar(returnType: type), |
| functionTypeStar(returnType: voidType), |
| ); |
| } |
| } |
| } |
| |
| //class Mix with ElementsTypesMixin { |
| // TypeProvider typeProvider; |
| // Dart2TypeSystem typeSystem; |
| // |
| // FeatureSet get testFeatureSet { |
| // return FeatureSet.forTesting(); |
| // } |
| // |
| // void setUp() { |
| // var analysisContext = TestAnalysisContext( |
| // featureSet: testFeatureSet, |
| // ); |
| // typeProvider = analysisContext.typeProvider; |
| // typeSystem = analysisContext.typeSystem; |
| // } |
| //} |
| |
| 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) { |
| var strType1 = _toStringWithNullability(type1); |
| var strType2 = _toStringWithNullability(type2); |
| expect(typeSystem.isSubtypeOf(type1, type2), false, |
| reason: '$strType1 was not supposed to be a subtype of $strType2'); |
| } |
| |
| 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); |
| } |
| |
| static String _toStringWithNullability(DartType type) { |
| return (type as TypeImpl).toString(withNullability: true); |
| } |
| } |
| |
| @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 functionTypeNone( |
| returnType: voidType, |
| ); |
| } |
| |
| FunctionTypeImpl get nothingToVoidFunctionTypeQuestion { |
| return functionTypeQuestion( |
| returnType: voidType, |
| ); |
| } |
| |
| FunctionTypeImpl get nothingToVoidFunctionTypeStar { |
| return functionTypeStar( |
| returnType: voidType, |
| ); |
| } |
| |
| 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(neverNone), 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(neverNone), false); |
| } |
| |
| test_isNullable_never() { |
| expect(typeSystem.isNullable(neverNone), 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(neverNone), 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(neverNone), 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(neverNone), neverNone); |
| } |
| |
| test_promoteToNonNull_null() { |
| expect(typeSystem.promoteToNonNull(nullType), neverNone); |
| } |
| |
| test_promoteToNonNull_typeParameter_noneBound_none() { |
| var element = typeParameter('T', bound: noneType); |
| var type = typeParameterTypeNone(element); |
| expect(typeSystem.promoteToNonNull(type), same(type)); |
| } |
| |
| test_promoteToNonNull_typeParameter_noneBound_question() { |
| var element = typeParameter('T', bound: stringClassTypeNone); |
| var type = typeParameterTypeQuestion(element); |
| _assertPromotedTypeParameterType( |
| typeSystem.promoteToNonNull(type), |
| baseElement: element, |
| expectedNullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| |
| test_promoteToNonNull_typeParameter_nullBound_none() { |
| var element = typeParameter('T', bound: null); |
| var type = typeParameterTypeNone(element); |
| _assertPromotedTypeParameterType( |
| typeSystem.promoteToNonNull(type), |
| baseElement: element, |
| expectedBound: objectClassTypeNone, |
| expectedNullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| |
| test_promoteToNonNull_typeParameter_questionBound_none() { |
| var element = typeParameter('T', bound: stringClassTypeQuestion); |
| var type = typeParameterTypeNone(element); |
| _assertPromotedTypeParameterType( |
| typeSystem.promoteToNonNull(type), |
| baseElement: element, |
| expectedBound: stringClassTypeNone, |
| expectedNullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| |
| test_promoteToNonNull_typeParameter_questionBound_question() { |
| var element = typeParameter('T', bound: stringClassTypeQuestion); |
| var type = typeParameterTypeQuestion(element); |
| _assertPromotedTypeParameterType( |
| typeSystem.promoteToNonNull(type), |
| baseElement: element, |
| expectedBound: stringClassTypeNone, |
| expectedNullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| |
| test_promoteToNonNull_typeParameter_questionBound_star() { |
| var element = typeParameter('T', bound: stringClassTypeQuestion); |
| var type = typeParameterTypeStar(element); |
| _assertPromotedTypeParameterType( |
| typeSystem.promoteToNonNull(type), |
| baseElement: element, |
| expectedBound: stringClassTypeNone, |
| expectedNullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| |
| test_promoteToNonNull_typeParameter_starBound_none() { |
| var element = typeParameter('T', bound: stringClassTypeStar); |
| var type = typeParameterTypeNone(element); |
| _assertPromotedTypeParameterType( |
| typeSystem.promoteToNonNull(type), |
| baseElement: element, |
| expectedBound: stringClassTypeNone, |
| expectedNullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| |
| test_promoteToNonNull_void() { |
| expect( |
| typeSystem.promoteToNonNull(voidType), |
| voidType, |
| ); |
| } |
| |
| /// If [expectedBound] is `null`, the element of [actual] must be the same |
| /// as the [baseElement]. Otherwise the element of [actual] must be a |
| /// [TypeParameterMember] with the [baseElement] and the [expectedBound]. |
| void _assertPromotedTypeParameterType( |
| TypeParameterTypeImpl actual, { |
| @required TypeParameterElement baseElement, |
| TypeImpl expectedBound, |
| @required NullabilitySuffix expectedNullabilitySuffix, |
| }) { |
| if (expectedBound != null) { |
| var actualMember = actual.element as TypeParameterMember; |
| expect(actualMember.declaration, same(baseElement)); |
| expect(actualMember.bound, expectedBound); |
| } else { |
| expect(actual.element, same(baseElement)); |
| } |
| expect(actual.nullabilitySuffix, expectedNullabilitySuffix); |
| } |
| |
| DartType _typeParameterTypeNone({@required DartType bound}) { |
| var element = typeParameter('T', bound: bound); |
| return typeParameterTypeNone(element); |
| } |
| |
| DartType _typeParameterTypeQuestion({@required DartType bound}) { |
| var element = typeParameter('T', bound: bound); |
| return typeParameterTypeQuestion(element); |
| } |
| |
| DartType _typeParameterTypeStar({@required DartType bound}) { |
| var element = typeParameter('T', bound: bound); |
| return typeParameterTypeStar(element); |
| } |
| } |