blob: 3929a49fa1b2fbdcaac502228d16be31621f9756 [file] [log] [blame]
// 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.
import 'package:analyzer/dart/analysis/features.dart';
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/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/least_upper_bound.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/dart/resolver/variance.dart';
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(LeastUpperBoundFunctionsTest);
defineReflectiveTests(LeastUpperBoundTest);
defineReflectiveTests(TryPromoteToTest);
});
}
abstract class AbstractTypeSystemNullSafetyTest with ElementsTypesMixin {
TestAnalysisContext analysisContext;
@override
LibraryElementImpl testLibrary;
@override
TypeProvider typeProvider;
TypeSystemImpl typeSystem;
FeatureSet get testFeatureSet {
return FeatureSet.forTesting(
additionalFeatures: [Feature.non_nullable],
);
}
void setUp() {
analysisContext = TestAnalysisContext(
featureSet: testFeatureSet,
);
typeProvider = analysisContext.typeProviderNonNullableByDefault;
typeSystem = analysisContext.typeSystemNonNullableByDefault;
testLibrary = library_(
uriStr: 'package:test/test.dart',
analysisSession: analysisContext.analysisSession,
typeSystem: typeSystem,
);
}
}
abstract class AbstractTypeSystemTest with ElementsTypesMixin {
TestAnalysisContext analysisContext;
@override
LibraryElementImpl testLibrary;
@override
TypeProvider typeProvider;
TypeSystemImpl typeSystem;
FeatureSet get testFeatureSet {
return FeatureSet.forTesting();
}
void setUp() {
analysisContext = TestAnalysisContext(
featureSet: testFeatureSet,
);
typeProvider = analysisContext.typeProviderLegacy;
typeSystem = analysisContext.typeSystemLegacy;
testLibrary = library_(
uriStr: 'package:test/test.dart',
analysisSession: analysisContext.analysisSession,
typeSystem: typeSystem,
);
}
String _typeString(TypeImpl type) {
return type.getDisplayString(withNullability: true);
}
}
@reflectiveTest
class AssignabilityTest extends AbstractTypeSystemTest {
void test_isAssignableTo_bottom_isBottom() {
var A = class_(name: 'A');
List<DartType> interassignable = <DartType>[
dynamicType,
objectStar,
intStar,
doubleStar,
numStar,
stringStar,
interfaceTypeStar(A),
neverStar,
];
_checkGroups(neverStar, interassignable: interassignable);
}
void test_isAssignableTo_call_method() {
var B = class_(
name: 'B',
methods: [
method('call', objectStar, parameters: [
requiredParameter(name: '_', type: intStar),
]),
],
);
B.enclosingElement = testLibrary.definingCompilationUnit;
_checkIsStrictAssignableTo(
interfaceTypeStar(B),
functionTypeStar(
parameters: [
requiredParameter(type: intStar),
],
returnType: objectStar,
),
);
}
void test_isAssignableTo_classes() {
var classTop = class_(name: 'A');
var classLeft = class_(name: 'B', superType: interfaceTypeStar(classTop));
var classRight = class_(name: 'C', superType: interfaceTypeStar(classTop));
var classBottom = class_(
name: 'D',
superType: interfaceTypeStar(classLeft),
interfaces: [interfaceTypeStar(classRight)],
);
var top = interfaceTypeStar(classTop);
var left = interfaceTypeStar(classLeft);
var right = interfaceTypeStar(classRight);
var bottom = interfaceTypeStar(classBottom);
_checkLattice(top, left, right, bottom);
}
void test_isAssignableTo_double() {
var A = class_(name: 'A');
List<DartType> interassignable = <DartType>[
dynamicType,
objectStar,
doubleStar,
numStar,
neverStar,
];
List<DartType> unrelated = <DartType>[
intStar,
stringStar,
interfaceTypeStar(A),
];
_checkGroups(doubleStar,
interassignable: interassignable, unrelated: unrelated);
}
void test_isAssignableTo_dynamic_isTop() {
var A = class_(name: 'A');
List<DartType> interassignable = <DartType>[
dynamicType,
objectStar,
intStar,
doubleStar,
numStar,
stringStar,
interfaceTypeStar(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: [
interfaceTypeStar(
L,
typeArguments: [
typeParameterTypeStar(MT),
],
),
],
);
var top = interfaceTypeStar(L, typeArguments: [dynamicType]);
var left = interfaceTypeStar(M, typeArguments: [dynamicType]);
var right = interfaceTypeStar(L, typeArguments: [intStar]);
var bottom = interfaceTypeStar(M, typeArguments: [intStar]);
_checkCrossLattice(top, left, right, bottom);
}
void test_isAssignableTo_int() {
var A = class_(name: 'A');
List<DartType> interassignable = <DartType>[
dynamicType,
objectStar,
intStar,
numStar,
neverStar,
];
List<DartType> unrelated = <DartType>[
doubleStar,
stringStar,
interfaceTypeStar(A),
];
_checkGroups(intStar,
interassignable: interassignable, unrelated: unrelated);
}
void test_isAssignableTo_named_optional() {
var r = functionTypeStar(
parameters: [
requiredParameter(type: intStar),
],
returnType: intStar,
);
var o = functionTypeStar(
parameters: [
positionalParameter(type: intStar),
],
returnType: intStar,
);
var n = functionTypeStar(
parameters: [
namedParameter(name: 'x', type: intStar),
],
returnType: intStar,
);
var rr = functionTypeStar(
parameters: [
requiredParameter(type: intStar),
requiredParameter(type: intStar),
],
returnType: intStar,
);
var ro = functionTypeStar(
parameters: [
requiredParameter(type: intStar),
positionalParameter(type: intStar),
],
returnType: intStar,
);
var rn = functionTypeStar(
parameters: [
requiredParameter(type: intStar),
namedParameter(name: 'x', type: intStar),
],
returnType: intStar,
);
var oo = functionTypeStar(
parameters: [
positionalParameter(type: intStar),
positionalParameter(type: intStar),
],
returnType: intStar,
);
var nn = functionTypeStar(
parameters: [
namedParameter(name: 'x', type: intStar),
namedParameter(name: 'y', type: intStar),
],
returnType: intStar,
);
var nnn = functionTypeStar(
parameters: [
namedParameter(name: 'x', type: intStar),
namedParameter(name: 'y', type: intStar),
namedParameter(name: 'z', type: intStar),
],
returnType: intStar,
);
_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,
objectStar,
numStar,
intStar,
doubleStar,
neverStar,
];
List<DartType> unrelated = <DartType>[
stringStar,
interfaceTypeStar(A),
];
_checkGroups(numStar,
interassignable: interassignable, unrelated: unrelated);
}
void test_isAssignableTo_simple_function() {
var top = functionTypeStar(
parameters: [
requiredParameter(type: intStar),
],
returnType: objectStar,
);
var left = functionTypeStar(
parameters: [
requiredParameter(type: intStar),
],
returnType: intStar,
);
var right = functionTypeStar(
parameters: [
requiredParameter(type: objectStar),
],
returnType: objectStar,
);
var bottom = functionTypeStar(
parameters: [
requiredParameter(type: objectStar),
],
returnType: intStar,
);
_checkCrossLattice(top, left, right, bottom);
}
void test_isAssignableTo_void_functions() {
var top = functionTypeStar(
parameters: [
requiredParameter(type: intStar),
],
returnType: voidNone,
);
var bottom = functionTypeStar(
parameters: [
requiredParameter(type: objectStar),
],
returnType: intStar,
);
_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.isAssignableTo2(type1, type2), true);
}
void _checkIsNotAssignableTo(DartType type1, DartType type2) {
expect(typeSystem.isAssignableTo2(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 _checkLeastUpperBound(DartType T1, DartType T2, DartType expected) {
var expectedStr = _typeString(expected);
var result = typeSystem.getLeastUpperBound(T1, T2);
var resultStr = _typeString(result);
expect(result, expected, reason: '''
expected: $expectedStr
actual: $resultStr
''');
// Check that the result is an upper bound.
expect(typeSystem.isSubtypeOf(T1, result), true);
expect(typeSystem.isSubtypeOf(T2, result), true);
// Check for symmetry.
result = typeSystem.getLeastUpperBound(T2, T1);
resultStr = _typeString(result);
expect(result, expected, reason: '''
expected: $expectedStr
actual: $resultStr
''');
}
}
@reflectiveTest
class LeastUpperBoundFunctionsTest extends BoundTestBase {
void test_differentRequiredArity() {
var type1 = functionTypeStar(
parameters: [
requiredParameter(type: intStar),
requiredParameter(type: intStar),
],
returnType: voidNone,
);
var type2 = functionTypeStar(
parameters: [
requiredParameter(type: intStar),
requiredParameter(type: intStar),
requiredParameter(type: intStar),
],
returnType: voidNone,
);
_checkLeastUpperBound(type1, type2, typeProvider.functionType);
}
void test_fuzzyArrows() {
var type1 = functionTypeStar(
parameters: [
requiredParameter(type: dynamicType),
],
returnType: voidNone,
);
var type2 = functionTypeStar(
parameters: [
requiredParameter(type: intStar),
],
returnType: voidNone,
);
var expected = functionTypeStar(
parameters: [
requiredParameter(type: intStar),
],
returnType: voidNone,
);
_checkLeastUpperBound(type1, type2, expected);
}
void test_glbNamedParams() {
var type1 = functionTypeStar(
parameters: [
namedParameter(name: 'a', type: stringStar),
namedParameter(name: 'b', type: intStar),
],
returnType: voidNone,
);
var type2 = functionTypeStar(
parameters: [
namedParameter(name: 'a', type: intStar),
namedParameter(name: 'b', type: numStar),
],
returnType: voidNone,
);
var expected = functionTypeStar(
parameters: [
namedParameter(name: 'a', type: neverStar),
namedParameter(name: 'b', type: intStar),
],
returnType: voidNone,
);
_checkLeastUpperBound(type1, type2, expected);
}
void test_glbPositionalParams() {
var type1 = functionTypeStar(
parameters: [
positionalParameter(type: stringStar),
positionalParameter(type: intStar),
],
returnType: voidNone,
);
var type2 = functionTypeStar(
parameters: [
positionalParameter(type: intStar),
positionalParameter(type: numStar),
],
returnType: voidNone,
);
var expected = functionTypeStar(
parameters: [
positionalParameter(type: neverStar),
positionalParameter(type: intStar),
],
returnType: voidNone,
);
_checkLeastUpperBound(type1, type2, expected);
}
void test_glbRequiredParams() {
var type1 = functionTypeStar(
parameters: [
requiredParameter(type: stringStar),
requiredParameter(type: intStar),
requiredParameter(type: intStar),
],
returnType: voidNone,
);
var type2 = functionTypeStar(
parameters: [
requiredParameter(type: intStar),
requiredParameter(type: doubleStar),
requiredParameter(type: numStar),
],
returnType: voidNone,
);
var expected = functionTypeStar(
parameters: [
requiredParameter(type: neverStar),
requiredParameter(type: neverStar),
requiredParameter(type: intStar),
],
returnType: voidNone,
);
_checkLeastUpperBound(type1, type2, expected);
}
void test_ignoreExtraNamedParams() {
var type1 = functionTypeStar(
parameters: [
namedParameter(name: 'a', type: intStar),
namedParameter(name: 'b', type: intStar),
],
returnType: voidNone,
);
var type2 = functionTypeStar(
parameters: [
namedParameter(name: 'a', type: intStar),
namedParameter(name: 'c', type: intStar),
],
returnType: voidNone,
);
var expected = functionTypeStar(
parameters: [
namedParameter(name: 'a', type: intStar),
],
returnType: voidNone,
);
_checkLeastUpperBound(type1, type2, expected);
}
void test_ignoreExtraPositionalParams() {
var type1 = functionTypeStar(
parameters: [
positionalParameter(type: intStar),
positionalParameter(type: intStar),
positionalParameter(type: stringStar),
],
returnType: voidNone,
);
var type2 = functionTypeStar(
parameters: [
positionalParameter(type: intStar),
],
returnType: voidNone,
);
var expected = functionTypeStar(
parameters: [
positionalParameter(type: intStar),
],
returnType: voidNone,
);
_checkLeastUpperBound(type1, type2, expected);
}
void test_lubReturnType() {
var type1 = functionTypeStar(returnType: intStar);
var type2 = functionTypeStar(returnType: doubleStar);
var expected = functionTypeStar(returnType: numStar);
_checkLeastUpperBound(type1, type2, expected);
}
void test_sameType_withNamed() {
var type1 = functionTypeStar(
parameters: [
requiredParameter(type: stringStar),
requiredParameter(type: intStar),
requiredParameter(type: numStar),
namedParameter(name: 'n', type: numStar),
],
returnType: intStar,
);
var type2 = functionTypeStar(
parameters: [
requiredParameter(type: stringStar),
requiredParameter(type: intStar),
requiredParameter(type: numStar),
namedParameter(name: 'n', type: numStar),
],
returnType: intStar,
);
var expected = functionTypeStar(
parameters: [
requiredParameter(type: stringStar),
requiredParameter(type: intStar),
requiredParameter(type: numStar),
namedParameter(name: 'n', type: numStar),
],
returnType: intStar,
);
_checkLeastUpperBound(type1, type2, expected);
}
void test_sameType_withOptional() {
var type1 = functionTypeStar(
parameters: [
requiredParameter(type: stringStar),
requiredParameter(type: intStar),
requiredParameter(type: numStar),
positionalParameter(type: doubleStar),
],
returnType: intStar,
);
var type2 = functionTypeStar(
parameters: [
requiredParameter(type: stringStar),
requiredParameter(type: intStar),
requiredParameter(type: numStar),
positionalParameter(type: doubleStar),
],
returnType: intStar,
);
var expected = functionTypeStar(
parameters: [
requiredParameter(type: stringStar),
requiredParameter(type: intStar),
requiredParameter(type: numStar),
positionalParameter(type: doubleStar),
],
returnType: intStar,
);
_checkLeastUpperBound(type1, type2, expected);
}
void test_typeFormals_differentBounds() {
var T1 = typeParameter('T1', bound: intStar);
var type1 = functionTypeStar(
typeFormals: [T1],
returnType: typeParameterTypeStar(T1),
);
var T2 = typeParameter('T2', bound: doubleStar);
var type2 = functionTypeStar(
typeFormals: [T2],
returnType: typeParameterTypeStar(T2),
);
_checkLeastUpperBound(type1, type2, typeProvider.functionType);
}
void test_typeFormals_differentNumber() {
var T1 = typeParameter('T1', bound: numStar);
var type1 = functionTypeStar(
typeFormals: [T1],
returnType: typeParameterTypeStar(T1),
);
var type2 = functionTypeStar(returnType: intStar);
_checkLeastUpperBound(type1, type2, typeProvider.functionType);
}
void test_typeFormals_sameBounds() {
var T1 = typeParameter('T1', bound: numStar);
var type1 = functionTypeStar(
typeFormals: [T1],
returnType: typeParameterTypeStar(T1),
);
var T2 = typeParameter('T2', bound: numStar);
var type2 = functionTypeStar(
typeFormals: [T2],
returnType: typeParameterTypeStar(T2),
);
var TE = typeParameter('T', bound: numStar);
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: voidNone),
functionTypeStar(returnType: voidNone));
}
void test_bottom_interface() {
var A = class_(name: 'A');
var typeA = interfaceTypeStar(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 = interfaceTypeStar(A);
var B = class_(name: 'B', interfaces: [typeA]);
var typeB = interfaceTypeStar(B);
var C = class_(name: 'C', interfaces: [typeB]);
var typeC = interfaceTypeStar(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 = interfaceTypeStar(A);
var B = class_(name: 'B', superType: typeA);
var typeB = interfaceTypeStar(B);
var C = class_(name: 'C', superType: typeB);
var typeC = interfaceTypeStar(C);
_checkLeastUpperBound(typeB, typeC, typeB);
}
void test_directSuperclass_nullability() {
var aElement = class_(name: 'A');
var aQuestion = interfaceTypeQuestion(aElement);
var aStar = interfaceTypeStar(aElement);
var aNone = interfaceTypeNone(aElement);
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: voidNone), dynamicType);
}
void test_dynamic_interface() {
var A = class_(name: 'A');
_checkLeastUpperBound(dynamicType, interfaceTypeStar(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, voidNone, voidNone);
}
void test_interface_function() {
var A = class_(name: 'A');
_checkLeastUpperBound(interfaceTypeStar(A),
functionTypeStar(returnType: voidNone), objectStar);
}
void test_interface_sameElement_nullability() {
var aElement = class_(name: 'A');
var aQuestion = interfaceTypeQuestion(aElement);
var aStar = interfaceTypeStar(aElement);
var aNone = interfaceTypeNone(aElement);
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 = mixin_(
name: 'M',
constraints: [instA.withNullabilitySuffixNone],
);
_checkLeastUpperBound(
interfaceTypeStar(classB),
interfaceTypeStar(mixinM),
instA.withNullability(NullabilitySuffix.star),
);
}
void test_mixinAndClass_object() {
var classA = class_(name: 'A');
var mixinM = mixin_(name: 'M');
_checkLeastUpperBound(
interfaceTypeStar(classA),
interfaceTypeStar(mixinM),
objectStar,
);
}
void test_mixinAndClass_sharedInterface() {
var classA = class_(name: 'A');
var instA = InstantiatedClass(classA, []);
var classB = class_(
name: 'B',
interfaces: [instA.withNullabilitySuffixNone],
);
var mixinM = mixin_(
name: 'M',
interfaces: [instA.withNullabilitySuffixNone],
);
_checkLeastUpperBound(
interfaceTypeStar(classB),
interfaceTypeStar(mixinM),
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 = interfaceTypeStar(A);
var B = class_(name: 'B', superType: typeA);
var typeB = interfaceTypeStar(B);
var C = class_(name: 'C', superType: typeA);
var typeC = interfaceTypeStar(C);
var D = class_(
name: 'D',
superType: typeB,
mixins: [
interfaceTypeStar(class_(name: 'M')),
interfaceTypeStar(class_(name: 'N')),
interfaceTypeStar(class_(name: 'O')),
interfaceTypeStar(class_(name: 'P')),
],
);
var typeD = interfaceTypeStar(D);
_checkLeastUpperBound(typeD, typeC, typeA);
}
void test_nestedFunctionsLubInnerParamTypes() {
var type1 = functionTypeStar(
parameters: [
requiredParameter(
type: functionTypeStar(
parameters: [
requiredParameter(type: stringStar),
requiredParameter(type: intStar),
requiredParameter(type: intStar),
],
returnType: voidNone,
),
),
],
returnType: voidNone,
);
var type2 = functionTypeStar(
parameters: [
requiredParameter(
type: functionTypeStar(
parameters: [
requiredParameter(type: intStar),
requiredParameter(type: doubleStar),
requiredParameter(type: numStar),
],
returnType: voidNone,
),
),
],
returnType: voidNone,
);
var expected = functionTypeStar(
parameters: [
requiredParameter(
type: functionTypeStar(
parameters: [
requiredParameter(type: objectStar),
requiredParameter(type: numStar),
requiredParameter(type: numStar),
],
returnType: voidNone,
),
),
],
returnType: voidNone,
);
_checkLeastUpperBound(type1, type2, expected);
}
void test_nestedNestedFunctionsGlbInnermostParamTypes() {
var type1 = functionTypeStar(
parameters: [
requiredParameter(
type: functionTypeStar(
parameters: [
requiredParameter(
type: functionTypeStar(
parameters: [
requiredParameter(type: stringStar),
requiredParameter(type: intStar),
requiredParameter(type: intStar)
],
returnType: voidNone,
),
),
],
returnType: voidNone,
),
),
],
returnType: voidNone,
);
expect(
_typeString(type1),
'void Function(void Function(void Function(String*, int*, int*)*)*)*',
);
var type2 = functionTypeStar(
parameters: [
requiredParameter(
type: functionTypeStar(
parameters: [
requiredParameter(
type: functionTypeStar(
parameters: [
requiredParameter(type: intStar),
requiredParameter(type: doubleStar),
requiredParameter(type: numStar)
],
returnType: voidNone,
),
),
],
returnType: voidNone,
),
),
],
returnType: voidNone,
);
expect(
_typeString(type2),
'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: intStar)
],
returnType: voidNone,
),
),
],
returnType: voidNone,
),
),
],
returnType: voidNone,
);
expect(
_typeString(expected),
'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 = interfaceTypeStar(A);
var typeB = interfaceTypeStar(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,
voidNone,
neverStar,
typeParameterTypeStar(T),
interfaceTypeStar(A),
functionTypeStar(returnType: voidNone)
];
for (DartType type in types) {
_checkLeastUpperBound(type, type, type);
}
}
void test_sharedSuperclass1() {
var A = class_(name: 'A');
var typeA = interfaceTypeStar(A);
var B = class_(name: 'B', superType: typeA);
var typeB = interfaceTypeStar(B);
var C = class_(name: 'C', superType: typeA);
var typeC = interfaceTypeStar(C);
_checkLeastUpperBound(typeB, typeC, typeA);
}
void test_sharedSuperclass1_nullability() {
var aElement = class_(name: 'A');
var aQuestion = interfaceTypeQuestion(aElement);
var aStar = interfaceTypeStar(aElement);
var aNone = interfaceTypeNone(aElement);
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 = interfaceTypeStar(A);
var B = class_(name: 'B', superType: typeA);
var typeB = interfaceTypeStar(B);
var C = class_(name: 'C', superType: typeA);
var typeC = interfaceTypeStar(C);
var D = class_(name: 'D', superType: typeC);
var typeD = interfaceTypeStar(D);
_checkLeastUpperBound(typeB, typeD, typeA);
}
void test_sharedSuperclass3() {
var A = class_(name: 'A');
var typeA = interfaceTypeStar(A);
var B = class_(name: 'B', superType: typeA);
var typeB = interfaceTypeStar(B);
var C = class_(name: 'C', superType: typeB);
var typeC = interfaceTypeStar(C);
var D = class_(name: 'D', superType: typeB);
var typeD = interfaceTypeStar(D);
_checkLeastUpperBound(typeC, typeD, typeB);
}
void test_sharedSuperclass4() {
var A = class_(name: 'A');
var typeA = interfaceTypeStar(A);
var A2 = class_(name: 'A2');
var typeA2 = interfaceTypeStar(A2);
var A3 = class_(name: 'A3');
var typeA3 = interfaceTypeStar(A3);
var B = class_(name: 'B', superType: typeA, interfaces: [typeA2]);
var typeB = interfaceTypeStar(B);
var C = class_(name: 'C', superType: typeA, interfaces: [typeA3]);
var typeC = interfaceTypeStar(C);
_checkLeastUpperBound(typeB, typeC, typeA);
}
void test_sharedSuperinterface1() {
var A = class_(name: 'A');
var typeA = interfaceTypeStar(A);
var B = class_(name: 'B', interfaces: [typeA]);
var typeB = interfaceTypeStar(B);
var C = class_(name: 'C', interfaces: [typeA]);
var typeC = interfaceTypeStar(C);
_checkLeastUpperBound(typeB, typeC, typeA);
}
void test_sharedSuperinterface2() {
var A = class_(name: 'A');
var typeA = interfaceTypeStar(A);
var B = class_(name: 'B', interfaces: [typeA]);
var typeB = interfaceTypeStar(B);
var C = class_(name: 'C', interfaces: [typeA]);
var typeC = interfaceTypeStar(C);
var D = class_(name: 'D', interfaces: [typeC]);
var typeD = interfaceTypeStar(D);
_checkLeastUpperBound(typeB, typeD, typeA);
}
void test_sharedSuperinterface3() {
var A = class_(name: 'A');
var typeA = interfaceTypeStar(A);
var B = class_(name: 'B', interfaces: [typeA]);
var typeB = interfaceTypeStar(B);
var C = class_(name: 'C', interfaces: [typeB]);
var typeC = interfaceTypeStar(C);
var D = class_(name: 'D', interfaces: [typeB]);
var typeD = interfaceTypeStar(D);
_checkLeastUpperBound(typeC, typeD, typeB);
}
void test_sharedSuperinterface4() {
var A = class_(name: 'A');
var typeA = interfaceTypeStar(A);
var A2 = class_(name: 'A2');
var typeA2 = interfaceTypeStar(A2);
var A3 = class_(name: 'A3');
var typeA3 = interfaceTypeStar(A3);
var B = class_(name: 'B', interfaces: [typeA, typeA2]);
var typeB = interfaceTypeStar(B);
var C = class_(name: 'C', interfaces: [typeA, typeA3]);
var typeC = interfaceTypeStar(C);
_checkLeastUpperBound(typeB, typeC, typeA);
}
void test_twoComparables() {
_checkLeastUpperBound(stringStar, numStar, objectStar);
}
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: interfaceTypeStar(A));
_checkLeastUpperBound(typeParameterTypeStar(T),
functionTypeStar(returnType: voidNone), objectStar);
}
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 = interfaceTypeStar(A, typeArguments: [typeS]);
var U = typeParameter('U');
var typeU = typeParameterTypeStar(U);
U.bound = interfaceTypeStar(A, typeArguments: [typeU]);
_checkLeastUpperBound(
typeS,
typeParameterTypeStar(U),
interfaceTypeStar(A, typeArguments: [objectStar]),
);
}
void test_typeParam_function_bounded() {
var T = typeParameter('T', bound: typeProvider.functionType);
_checkLeastUpperBound(
typeParameterTypeStar(T),
functionTypeStar(returnType: voidNone),
typeProvider.functionType,
);
}
void test_typeParam_function_noBound() {
var T = typeParameter('T');
_checkLeastUpperBound(
typeParameterTypeStar(T),
functionTypeStar(returnType: voidNone),
objectStar,
);
}
void test_typeParam_interface_bounded() {
var A = class_(name: 'A');
var typeA = interfaceTypeStar(A);
var B = class_(name: 'B', superType: typeA);
var typeB = interfaceTypeStar(B);
var C = class_(name: 'C', superType: typeA);
var typeC = interfaceTypeStar(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),
interfaceTypeStar(A),
objectStar,
);
}
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 = interfaceTypeStar(A, typeArguments: [numStar]);
var aInt = interfaceTypeStar(A, typeArguments: [intStar]);
_checkLeastUpperBound(aInt, aNum, aInt);
}
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 = interfaceTypeStar(A, typeArguments: [numStar]);
_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 = interfaceTypeStar(A, typeArguments: [numStar]);
var aInt = interfaceTypeStar(A, typeArguments: [intStar]);
_checkLeastUpperBound(aInt, aNum, aNum);
}
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 = interfaceTypeStar(A, typeArguments: [numStar]);
_checkLeastUpperBound(aNum, aNum, aNum);
}
/// Check least upper bound of the same class with different type parameters.
void test_typeParameters_different() {
// class List<int>
// class List<double>
var listOfIntType = listStar(intStar);
var listOfDoubleType = listStar(doubleStar);
var listOfNum = listStar(numStar);
_checkLeastUpperBound(listOfIntType, listOfDoubleType, listOfNum);
}
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 = interfaceTypeStar(A, typeArguments: [numStar]);
var aInt = interfaceTypeStar(A, typeArguments: [intStar]);
_checkLeastUpperBound(aNum, aInt, objectStar);
}
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 = interfaceTypeStar(A, typeArguments: [numStar]);
_checkLeastUpperBound(aNum, aNum, aNum);
}
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 =
interfaceTypeStar(Multi, typeArguments: [numStar, numStar, numStar]);
var multiIntNumInt =
interfaceTypeStar(Multi, typeArguments: [intStar, numStar, intStar]);
// We expect Multi<num, num, int>
var multiNumNumInt =
interfaceTypeStar(Multi, typeArguments: [numStar, numStar, intStar]);
_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 =
interfaceTypeStar(Multi, typeArguments: [numStar, stringStar, numStar]);
var multiIntNumInt =
interfaceTypeStar(Multi, typeArguments: [intStar, numStar, intStar]);
_checkLeastUpperBound(multiNumStringNum, multiIntNumInt, objectStar);
}
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 =
interfaceTypeStar(Multi, typeArguments: [stringStar, numStar, numStar]);
var multiIntNumInt =
interfaceTypeStar(Multi, typeArguments: [intStar, numStar, intStar]);
// We expect Multi<Object, num, int>
var multiObjectNumInt =
interfaceTypeStar(Multi, typeArguments: [objectStar, numStar, intStar]);
_checkLeastUpperBound(multiStringNumNum, multiIntNumInt, multiObjectNumInt);
}
void test_typeParameters_same() {
// List<int>
// List<int>
var listOfIntType = listStar(intStar);
_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 = listStar(intStar);
var iterableOfDoubleType = iterableStar(doubleStar);
// TODO(leafp): this should be iterableOfNumType
_checkLeastUpperBound(listOfIntType, iterableOfDoubleType, objectStar);
}
void test_void() {
var T = typeParameter('T');
var A = class_(name: 'A');
List<DartType> types = [
neverStar,
functionTypeStar(returnType: voidNone),
interfaceTypeStar(A),
typeParameterTypeStar(T),
];
for (DartType type in types) {
_checkLeastUpperBound(
functionTypeStar(returnType: voidNone),
functionTypeStar(returnType: type),
functionTypeStar(returnType: voidNone),
);
}
}
}
@reflectiveTest
class TryPromoteToTest extends AbstractTypeSystemTest {
@override
FeatureSet get testFeatureSet {
return FeatureSet.forTesting(
additionalFeatures: [Feature.non_nullable],
);
}
void notPromotes(DartType from, DartType to) {
var result = typeSystem.tryPromoteToType(to, from);
expect(result, isNull);
}
void promotes(DartType from, DartType to) {
var result = typeSystem.tryPromoteToType(to, from);
expect(result, to);
}
test_interface() {
promotes(intNone, intNone);
promotes(intQuestion, intNone);
promotes(intStar, intNone);
promotes(numNone, intNone);
promotes(numQuestion, intNone);
promotes(numStar, intNone);
notPromotes(intNone, doubleNone);
notPromotes(intNone, intQuestion);
}
test_typeParameter() {
void check(
TypeParameterTypeImpl type,
TypeParameterElement expectedElement,
DartType expectedBound,
) {
expect(type.element, expectedElement);
expect(type.promotedBound, expectedBound);
}
var T = typeParameter('T');
var T0 = typeParameterTypeNone(T);
var T1 = typeSystem.tryPromoteToType(numNone, T0);
check(T1, T, numNone);
var T2 = typeSystem.tryPromoteToType(intNone, T1);
check(T2, T, intNone);
}
}