blob: 9a7c535de32daa5279efd20fc7435268192e218e [file] [log] [blame]
// Copyright (c) 2019, 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/src/dart/element/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_visitor.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../../generated/elements_types_mixin.dart';
import '../../../generated/test_analysis_context.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(BoundsHelperPredicatesTest);
defineReflectiveTests(UpperBoundTest);
});
}
@reflectiveTest
class BoundsHelperPredicatesTest extends _SubtypingTestBase {
static final Map<String, StackTrace> _isMoreBottomChecked = {};
static final Map<String, StackTrace> _isMoreTopChecked = {};
@override
FeatureSet get testFeatureSet {
return FeatureSet.forTesting(
additionalFeatures: [Feature.non_nullable],
);
}
void isBottom(DartType type) {
expect(typeSystem.isBottom(type), isTrue, reason: _typeString(type));
}
void isMoreBottom(DartType T, DartType S) {
_assertIsBottomOrNull(T);
_assertIsBottomOrNull(S);
var str = _typeString(T) + ' vs ' + _typeString(S);
_checkUniqueTypeStr(_isMoreBottomChecked, str);
expect(typeSystem.isMoreBottom(T, S), isTrue, reason: str);
}
void isMoreTop(DartType T, DartType S) {
_assertIsTopOrObject(T);
_assertIsTopOrObject(S);
var str = _typeString(T) + ' vs ' + _typeString(S);
_checkUniqueTypeStr(_isMoreTopChecked, str);
expect(typeSystem.isMoreTop(T, S), isTrue, reason: str);
}
void isNotBottom(DartType type) {
expect(typeSystem.isBottom(type), isFalse, reason: _typeString(type));
}
void isNotMoreBottom(DartType T, DartType S) {
_assertIsBottomOrNull(T);
_assertIsBottomOrNull(S);
var str = _typeString(T) + ' vs ' + _typeString(S);
_checkUniqueTypeStr(_isMoreBottomChecked, str);
expect(typeSystem.isMoreBottom(T, S), isFalse, reason: str);
}
void isNotMoreTop(DartType T, DartType S) {
_assertIsTopOrObject(T);
_assertIsTopOrObject(S);
var str = _typeString(T) + ' vs ' + _typeString(S);
_checkUniqueTypeStr(_isMoreTopChecked, str);
expect(typeSystem.isMoreTop(T, S), isFalse, reason: str);
}
void isNotNull(DartType type) {
expect(typeSystem.isNull(type), isFalse, reason: _typeString(type));
}
void isNotObject(DartType type) {
expect(typeSystem.isObject(type), isFalse, reason: _typeString(type));
}
void isNotTop(DartType type) {
expect(typeSystem.isTop(type), isFalse, reason: _typeString(type));
}
void isNull(DartType type) {
expect(typeSystem.isNull(type), isTrue, reason: _typeString(type));
}
void isObject(DartType type) {
expect(typeSystem.isObject(type), isTrue, reason: _typeString(type));
}
void isTop(DartType type) {
expect(typeSystem.isTop(type), isTrue, reason: _typeString(type));
}
test_isBottom() {
TypeParameterElement T;
TypeParameterMember T2;
// BOTTOM(Never) is true
isBottom(neverNone);
isNotBottom(neverQuestion);
isNotBottom(neverStar);
// BOTTOM(X&T) is true iff BOTTOM(T)
T = typeParameter('T', bound: objectQuestion);
T2 = promoteTypeParameter(T, neverNone);
isBottom(typeParameterTypeNone(T2));
isBottom(typeParameterTypeQuestion(T2));
isBottom(typeParameterTypeStar(T2));
T2 = promoteTypeParameter(T, neverQuestion);
isNotBottom(typeParameterTypeNone(T2));
isNotBottom(typeParameterTypeQuestion(T2));
isNotBottom(typeParameterTypeStar(T2));
// BOTTOM(X extends T) is true iff BOTTOM(T)
T = typeParameter('T', bound: neverNone);
isBottom(typeParameterTypeNone(T));
isBottom(typeParameterTypeQuestion(T));
isBottom(typeParameterTypeStar(T));
T = typeParameter('T', bound: neverQuestion);
isNotBottom(typeParameterTypeNone(T));
isNotBottom(typeParameterTypeQuestion(T));
isNotBottom(typeParameterTypeStar(T));
// BOTTOM(T) is false otherwise
isNotBottom(dynamicNone);
isNotBottom(voidNone);
isNotBottom(objectNone);
isNotBottom(objectQuestion);
isNotBottom(objectStar);
isNotBottom(intNone);
isNotBottom(intQuestion);
isNotBottom(intStar);
T = typeParameter('T', bound: numNone);
isNotBottom(typeParameterTypeNone(T));
isNotBottom(typeParameterTypeQuestion(T));
isNotBottom(typeParameterTypeStar(T));
T = typeParameter('T', bound: numStar);
isNotBottom(typeParameterTypeNone(T));
isNotBottom(typeParameterTypeQuestion(T));
isNotBottom(typeParameterTypeStar(T));
T2 = promoteTypeParameter(typeParameter('T'), intNone);
isNotBottom(typeParameterTypeNone(T2));
isNotBottom(typeParameterTypeQuestion(T2));
isNotBottom(typeParameterTypeStar(T2));
}
test_isMoreBottom() {
// MOREBOTTOM(Never, T) = true
isMoreBottom(neverNone, neverNone);
isMoreBottom(neverNone, neverQuestion);
isMoreBottom(neverNone, neverStar);
isMoreBottom(neverNone, nullNone);
isMoreBottom(neverNone, nullQuestion);
isMoreBottom(neverNone, nullStar);
// MOREBOTTOM(T, Never) = false
isNotMoreBottom(neverQuestion, neverNone);
isNotMoreBottom(neverStar, neverNone);
isNotMoreBottom(nullNone, neverNone);
isNotMoreBottom(nullQuestion, neverNone);
isNotMoreBottom(nullStar, neverNone);
// MOREBOTTOM(Null, T) = true
isMoreBottom(nullNone, neverQuestion);
isMoreBottom(nullNone, neverStar);
isMoreBottom(nullNone, nullNone);
isMoreBottom(nullNone, nullQuestion);
isMoreBottom(nullNone, nullStar);
// MOREBOTTOM(T, Null) = false
isNotMoreBottom(neverQuestion, nullNone);
isNotMoreBottom(neverStar, nullNone);
isNotMoreBottom(nullQuestion, nullNone);
isNotMoreBottom(nullStar, nullNone);
// MOREBOTTOM(T?, S?) = MOREBOTTOM(T, S)
isMoreBottom(neverQuestion, nullQuestion);
isNotMoreBottom(nullQuestion, neverQuestion);
// MOREBOTTOM(T, S?) = true
isMoreBottom(neverStar, nullQuestion);
isMoreBottom(nullStar, neverQuestion);
// MOREBOTTOM(T?, S) = false
isNotMoreBottom(neverQuestion, nullStar);
isNotMoreBottom(nullQuestion, neverStar);
// MOREBOTTOM(T*, S*) = MOREBOTTOM(T, S)
isMoreBottom(neverStar, nullStar);
isNotMoreBottom(nullStar, neverStar);
// MOREBOTTOM(T, S*) = true
isMoreBottom(
typeParameterTypeNone(
typeParameter('S', bound: neverNone),
),
nullStar,
);
// MOREBOTTOM(T*, S) = false
isNotMoreBottom(
nullStar,
typeParameterTypeNone(
typeParameter('S', bound: neverNone),
),
);
// MOREBOTTOM(X&T, Y&S) = MOREBOTTOM(T, S)
isMoreBottom(
typeParameterTypeNone(
promoteTypeParameter(
typeParameter('T', bound: objectQuestion),
neverNone,
),
),
typeParameterTypeQuestion(
promoteTypeParameter(
typeParameter('S', bound: objectQuestion),
neverNone,
),
),
);
// MOREBOTTOM(X&T, S) = true
isMoreBottom(
typeParameterTypeNone(
promoteTypeParameter(
typeParameter('T', bound: objectQuestion),
neverNone,
),
),
typeParameterTypeNone(
typeParameter('S', bound: neverNone),
),
);
// MOREBOTTOM(T, X&S) = false
isNotMoreBottom(
typeParameterTypeNone(
typeParameter('T', bound: neverNone),
),
typeParameterTypeNone(
promoteTypeParameter(
typeParameter('S', bound: objectQuestion),
neverNone,
),
),
);
// MOREBOTTOM(X extends T, Y extends S) = MOREBOTTOM(T, S)
isMoreBottom(
typeParameterTypeNone(
typeParameter('T', bound: neverNone),
),
typeParameterTypeQuestion(
typeParameter('S', bound: neverNone),
),
);
}
test_isMoreTop() {
// MORETOP(void, T) = true
isMoreTop(voidNone, voidNone);
isMoreTop(voidNone, dynamicNone);
isMoreTop(voidNone, objectNone);
isMoreTop(voidNone, objectQuestion);
isMoreTop(voidNone, objectStar);
isMoreTop(voidNone, futureOrNone(objectNone));
isMoreTop(voidNone, futureOrNone(objectQuestion));
isMoreTop(voidNone, futureOrNone(objectStar));
// MORETOP(T, void) = false
isNotMoreTop(dynamicNone, voidNone);
isNotMoreTop(objectNone, voidNone);
isNotMoreTop(objectQuestion, voidNone);
isNotMoreTop(objectStar, voidNone);
isNotMoreTop(futureOrNone(objectNone), voidNone);
isNotMoreTop(futureOrNone(objectQuestion), voidNone);
isNotMoreTop(futureOrNone(objectStar), voidNone);
// MORETOP(dynamic, T) = true
isMoreTop(dynamicNone, dynamicNone);
isMoreTop(dynamicNone, objectNone);
isMoreTop(dynamicNone, objectQuestion);
isMoreTop(dynamicNone, objectStar);
isMoreTop(dynamicNone, futureOrNone(objectNone));
isMoreTop(dynamicNone, futureOrNone(objectQuestion));
isMoreTop(dynamicNone, futureOrNone(objectStar));
// MORETOP(T, dynamic) = false
isNotMoreTop(objectNone, dynamicNone);
isNotMoreTop(objectQuestion, dynamicNone);
isNotMoreTop(objectStar, dynamicNone);
isNotMoreTop(futureOrNone(objectNone), dynamicNone);
isNotMoreTop(futureOrNone(objectQuestion), dynamicNone);
isNotMoreTop(futureOrNone(objectStar), dynamicNone);
// MORETOP(Object, T) = true
isMoreTop(objectNone, objectNone);
isMoreTop(objectNone, objectQuestion);
isMoreTop(objectNone, objectStar);
isMoreTop(objectNone, futureOrNone(objectNone));
isMoreTop(objectNone, futureOrQuestion(objectNone));
isMoreTop(objectNone, futureOrStar(objectNone));
// MORETOP(T, Object) = false
isNotMoreTop(objectQuestion, objectNone);
isNotMoreTop(objectStar, objectNone);
isNotMoreTop(futureOrNone(objectNone), objectNone);
isNotMoreTop(futureOrQuestion(objectNone), objectNone);
isNotMoreTop(futureOrStar(objectNone), objectNone);
// MORETOP(T*, S*) = MORETOP(T, S)
isMoreTop(objectStar, objectStar);
isMoreTop(objectStar, futureOrStar(objectNone));
isMoreTop(objectStar, futureOrStar(objectQuestion));
isMoreTop(objectStar, futureOrStar(objectStar));
isMoreTop(futureOrStar(objectNone), futureOrStar(objectNone));
// MORETOP(T, S*) = true
isMoreTop(futureOrNone(objectNone), futureOrStar(voidNone));
isMoreTop(futureOrNone(objectNone), futureOrStar(dynamicNone));
isMoreTop(futureOrNone(objectNone), futureOrStar(objectNone));
isMoreTop(futureOrQuestion(objectNone), futureOrStar(voidNone));
isMoreTop(futureOrQuestion(objectNone), futureOrStar(dynamicNone));
isMoreTop(futureOrQuestion(objectNone), futureOrStar(objectNone));
// MORETOP(T*, S) = false
isNotMoreTop(futureOrStar(voidNone), futureOrNone(objectNone));
isNotMoreTop(futureOrStar(dynamicNone), futureOrNone(objectNone));
isNotMoreTop(futureOrStar(objectNone), futureOrNone(objectNone));
isNotMoreTop(futureOrStar(voidNone), futureOrQuestion(objectNone));
isNotMoreTop(futureOrStar(dynamicNone), futureOrQuestion(objectNone));
isNotMoreTop(futureOrStar(objectNone), futureOrQuestion(objectNone));
// MORETOP(T?, S?) = MORETOP(T, S)
isMoreTop(objectQuestion, objectQuestion);
isMoreTop(futureOrQuestion(voidNone), futureOrQuestion(voidNone));
isMoreTop(futureOrQuestion(voidNone), futureOrQuestion(dynamicNone));
isMoreTop(futureOrQuestion(voidNone), futureOrQuestion(objectNone));
// MORETOP(T, S?) = true
isMoreTop(futureOrNone(objectNone), futureOrQuestion(voidNone));
isMoreTop(futureOrNone(objectNone), futureOrQuestion(dynamicNone));
isMoreTop(futureOrNone(objectNone), futureOrQuestion(objectNone));
// MORETOP(T?, S) = false
isNotMoreTop(futureOrQuestion(voidNone), futureOrNone(objectNone));
isNotMoreTop(futureOrQuestion(dynamicNone), futureOrNone(objectNone));
isNotMoreTop(futureOrQuestion(objectNone), futureOrNone(objectNone));
// MORETOP(FutureOr<T>, FutureOr<S>) = MORETOP(T, S)
isMoreTop(futureOrNone(voidNone), futureOrNone(voidNone));
isMoreTop(futureOrNone(voidNone), futureOrNone(dynamicNone));
isMoreTop(futureOrNone(voidNone), futureOrNone(objectNone));
isNotMoreTop(futureOrNone(dynamicNone), futureOrNone(voidNone));
isNotMoreTop(futureOrNone(objectNone), futureOrNone(voidNone));
}
test_isNull() {
// NULL(Null) is true
isNull(nullNone);
// NULL(T?) is true iff NULL(T) or BOTTOM(T)
isNull(nullQuestion);
isNull(neverQuestion);
isNull(
typeParameterTypeQuestion(
typeParameter('T', bound: neverNone),
),
);
// NULL(T*) is true iff NULL(T) or BOTTOM(T)
isNull(nullStar);
isNull(neverStar);
isNull(
typeParameterTypeStar(
typeParameter('T', bound: neverNone),
),
);
// NULL(T) is false otherwise
isNotNull(dynamicNone);
isNotNull(voidNone);
isNotNull(objectNone);
isNotNull(objectQuestion);
isNotNull(objectStar);
isNotNull(intNone);
isNotNull(intQuestion);
isNotNull(intStar);
isNotNull(futureOrNone(nullNone));
isNotNull(futureOrNone(nullQuestion));
isNotNull(futureOrNone(nullStar));
isNotNull(futureOrQuestion(nullNone));
isNotNull(futureOrQuestion(nullQuestion));
isNotNull(futureOrQuestion(nullStar));
isNotNull(futureOrStar(nullNone));
isNotNull(futureOrStar(nullQuestion));
isNotNull(futureOrStar(nullStar));
}
test_isObject() {
// OBJECT(Object) is true
isObject(objectNone);
isNotObject(objectQuestion);
isNotObject(objectStar);
// OBJECT(FutureOr<T>) is OBJECT(T)
isObject(futureOrNone(objectNone));
isNotObject(futureOrNone(objectQuestion));
isNotObject(futureOrNone(objectStar));
isNotObject(futureOrQuestion(objectNone));
isNotObject(futureOrQuestion(objectQuestion));
isNotObject(futureOrQuestion(objectStar));
isNotObject(futureOrStar(objectNone));
isNotObject(futureOrStar(objectQuestion));
isNotObject(futureOrStar(objectStar));
// OBJECT(T) is false otherwise
isNotObject(dynamicNone);
isNotObject(voidNone);
isNotObject(intNone);
}
test_isTop() {
// TOP(T?) is true iff TOP(T) or OBJECT(T)
isTop(objectQuestion);
isTop(futureOrQuestion(dynamicNone));
isTop(futureOrQuestion(voidNone));
isTop(futureOrQuestion(objectNone));
isTop(futureOrQuestion(objectQuestion));
isTop(futureOrQuestion(objectStar));
isNotTop(futureOrQuestion(intNone));
isNotTop(futureOrQuestion(intQuestion));
isNotTop(futureOrQuestion(intStar));
// TOP(T*) is true iff TOP(T) or OBJECT(T)
isTop(objectStar);
isTop(futureOrStar(dynamicNone));
isTop(futureOrStar(voidNone));
isTop(futureOrStar(objectNone));
isTop(futureOrStar(objectQuestion));
isTop(futureOrStar(objectStar));
isNotTop(futureOrStar(intNone));
isNotTop(futureOrStar(intQuestion));
isNotTop(futureOrStar(intStar));
// TOP(dynamic) is true
isTop(dynamicNone);
// TOP(void) is true
isTop(voidNone);
// TOP(FutureOr<T>) is TOP(T)
isTop(futureOrNone(dynamicNone));
isTop(futureOrNone(voidNone));
isNotTop(futureOrNone(objectNone));
isTop(futureOrNone(objectQuestion));
isTop(futureOrNone(objectStar));
// TOP(T) is false otherwise
isNotTop(objectNone);
isNotTop(intNone);
isNotTop(intQuestion);
isNotTop(intStar);
isNotTop(neverNone);
isNotTop(neverQuestion);
isNotTop(neverStar);
}
/// [TypeSystemImpl.isMoreBottom] can be used only for `BOTTOM` or `NULL`
/// types. No need to check other types.
void _assertIsBottomOrNull(DartType type) {
expect(typeSystem.isBottom(type) || typeSystem.isNull(type), isTrue,
reason: _typeString(type));
}
/// [TypeSystemImpl.isMoreTop] can be used only for `TOP` or `OBJECT`
/// types. No need to check other types.
void _assertIsTopOrObject(DartType type) {
expect(typeSystem.isTop(type) || typeSystem.isObject(type), isTrue,
reason: _typeString(type));
}
void _checkUniqueTypeStr(Map<String, StackTrace> map, String str) {
var previousStack = map[str];
if (previousStack != null) {
fail('Not unique: $str\n$previousStack');
} else {
map[str] = StackTrace.current;
}
}
String _typeParametersStr(TypeImpl type) {
var typeStr = '';
var typeParameterCollector = _TypeParameterCollector();
DartTypeVisitor.visit(type, typeParameterCollector);
for (var typeParameter in typeParameterCollector.typeParameters) {
if (typeParameter is TypeParameterMember) {
var base = typeParameter.declaration;
var baseBound = base.bound as TypeImpl;
if (baseBound != null) {
var baseBoundStr = baseBound.toString(withNullability: true);
typeStr += ', ${typeParameter.name} extends ' + baseBoundStr;
}
var bound = typeParameter.bound as TypeImpl;
var boundStr = bound.toString(withNullability: true);
typeStr += ', ${typeParameter.name} & ' + boundStr;
} else {
var bound = typeParameter.bound as TypeImpl;
if (bound != null) {
var boundStr = bound.toString(withNullability: true);
typeStr += ', ${typeParameter.name} extends ' + boundStr;
}
}
}
return typeStr;
}
}
@reflectiveTest
class UpperBoundTest extends _SubtypingTestBase {
test_bottom_any() {
void check(DartType T1, DartType T2) {
expect(typeSystem.isBottom(T1), isTrue, reason: _typeString(T1));
expect(typeSystem.isBottom(T2), isFalse, reason: _typeString(T2));
_checkLeastUpperBound(T1, T2, T2);
}
check(neverNone, objectNone);
check(neverNone, objectStar);
check(neverNone, objectQuestion);
check(neverNone, intNone);
check(neverNone, intQuestion);
check(neverNone, intStar);
check(neverNone, listNone(intNone));
check(neverNone, listQuestion(intNone));
check(neverNone, listStar(intNone));
check(neverNone, futureOrNone(intNone));
check(neverNone, futureOrQuestion(intNone));
check(neverNone, futureOrStar(intNone));
{
var T = typeParameterTypeNone(
typeParameter('T', bound: neverNone),
);
check(T, intNone);
check(T, intQuestion);
check(T, intStar);
}
{
var T = typeParameterTypeNone(
promoteTypeParameter(
typeParameter('T', bound: objectQuestion),
neverNone,
),
);
check(T, intNone);
check(T, intQuestion);
check(T, intStar);
}
}
test_bottom_bottom() {
void check(DartType T1, DartType T2) {
expect(typeSystem.isBottom(T1), isTrue, reason: _typeString(T1));
expect(typeSystem.isBottom(T2), isTrue, reason: _typeString(T2));
_checkLeastUpperBound(T1, T2, T2);
}
check(
neverNone,
typeParameterTypeNone(
typeParameter('T', bound: neverNone),
),
);
check(
neverNone,
typeParameterTypeNone(
promoteTypeParameter(
typeParameter('T', bound: objectQuestion),
neverNone,
),
),
);
}
test_functionType2_parameters_named() {
FunctionType build(Map<String, DartType> namedTypes) {
return functionTypeNone(
returnType: voidNone,
parameters: namedTypes.entries.map((entry) {
return namedParameter(name: entry.key, type: entry.value);
}).toList(),
);
}
void check(Map<String, DartType> T1_named, Map<String, DartType> T2_named,
Map<String, DartType> expected_named) {
var T1 = build(T1_named);
var T2 = build(T2_named);
var expected = build(expected_named);
_checkLeastUpperBound(T1, T2, expected);
}
check({'a': intNone}, {}, {});
check({'a': intNone}, {'b': intNone}, {});
check({'a': intNone}, {'a': intNone}, {'a': intNone});
check({'a': intNone}, {'a': intQuestion}, {'a': intNone});
check({'a': intNone, 'b': doubleNone}, {'a': intNone}, {'a': intNone});
}
test_functionType2_parameters_optionalPositional() {
FunctionType build(List<DartType> positionalTypes) {
return functionTypeNone(
returnType: voidNone,
parameters: positionalTypes.map((type) {
return positionalParameter(type: type);
}).toList(),
);
}
void check(List<DartType> T1_positional, List<DartType> T2_positional,
DartType expected) {
var T1 = build(T1_positional);
var T2 = build(T2_positional);
_checkLeastUpperBound(T1, T2, expected);
}
check([intNone], [], build([]));
check([intNone, doubleNone], [intNone], build([intNone]));
check([intNone], [intNone], build([intNone]));
check([intNone], [intQuestion], build([intNone]));
// TODO(scheglov) Uncomment when DOWN is NNBD based.
// check([intNone], [intStar], build([intNone]));
// check([intNone], [doubleNone], build([neverNone]));
check([intNone], [numNone], build([intNone]));
check(
[doubleNone, numNone],
[numNone, intNone],
build([doubleNone, intNone]),
);
}
test_functionType2_parameters_required() {
FunctionType build(List<DartType> requiredTypes) {
return functionTypeNone(
returnType: voidNone,
parameters: requiredTypes.map((type) {
return requiredParameter(type: type);
}).toList(),
);
}
void check(List<DartType> T1_required, List<DartType> T2_required,
DartType expected) {
var T1 = build(T1_required);
var T2 = build(T2_required);
_checkLeastUpperBound(T1, T2, expected);
}
check([intNone], [], functionNone);
check([intNone], [intNone], build([intNone]));
check([intNone], [intQuestion], build([intNone]));
// TODO(scheglov) Uncomment when DOWN is NNBD based.
// check([intNone], [intStar], build([intNone]));
// check([intNone], [doubleNone], build([neverNone]));
check([intNone], [numNone], build([intNone]));
check(
[doubleNone, numNone],
[numNone, intNone],
build([doubleNone, intNone]),
);
}
test_functionType2_returnType() {
void check(DartType T1_ret, DartType T2_ret, DartType expected_ret) {
_checkLeastUpperBound(
functionTypeNone(returnType: T1_ret),
functionTypeNone(returnType: T2_ret),
functionTypeNone(returnType: expected_ret),
);
}
check(intNone, intNone, intNone);
check(intNone, intQuestion, intQuestion);
check(intNone, intStar, intStar);
check(intNone, numNone, numNone);
check(intQuestion, numNone, numQuestion);
check(intStar, numNone, numStar);
check(intNone, dynamicNone, dynamicNone);
check(intNone, neverNone, intNone);
}
test_functionType2_typeParameters() {
void check(FunctionType T1, FunctionType T2, DartType expected) {
_assertNullabilityNone(T1);
_assertNullabilityNone(T2);
_checkLeastUpperBound(T1, T2, expected);
}
check(
functionTypeNone(
returnType: voidNone,
typeFormals: [
typeParameter('T'),
],
),
functionTypeNone(returnType: voidNone),
functionNone,
);
check(
functionTypeNone(
returnType: voidNone,
typeFormals: [
typeParameter('T', bound: intNone),
],
),
functionTypeNone(
returnType: voidNone,
typeFormals: [
typeParameter('T', bound: numNone),
],
),
functionNone,
);
{
var T = typeParameter('T', bound: numNone);
var U = typeParameter('U', bound: numNone);
var R = typeParameter('R', bound: numNone);
check(
functionTypeNone(
returnType: typeParameterTypeNone(T),
typeFormals: [T],
),
functionTypeNone(
returnType: typeParameterTypeNone(U),
typeFormals: [U],
),
functionTypeNone(
returnType: typeParameterTypeNone(R),
typeFormals: [R],
),
);
}
}
test_functionType_interfaceType() {
void check(FunctionType T1, InterfaceType T2, InterfaceType expected) {
_checkLeastUpperBound(T1, T2, expected);
}
check(
functionTypeNone(returnType: voidNone),
intNone,
objectNone,
);
}
test_functionType_interfaceType_Function() {
void check(FunctionType T1, InterfaceType T2, InterfaceType expected) {
_checkLeastUpperBound(T1, T2, expected);
}
void checkNone(FunctionType T1) {
_assertNullabilityNone(T1);
check(T1, functionNone, functionNone);
}
checkNone(functionTypeNone(returnType: voidNone));
checkNone(
functionTypeNone(
returnType: intNone,
parameters: [
requiredParameter(type: numQuestion),
],
),
);
check(
functionTypeQuestion(returnType: voidNone),
functionNone,
functionQuestion,
);
}
test_identical() {
void check(DartType type) {
_checkLeastUpperBound(type, type, type);
}
check(intNone);
check(intQuestion);
check(intStar);
check(listNone(intNone));
}
test_none_question() {
void check(DartType T1, DartType T2, DartType expected) {
_assertNullabilityNone(T1);
_assertNullabilityQuestion(T2);
_assertNotSpecial(T1);
_assertNotSpecial(T2);
_checkLeastUpperBound(T1, T2, expected);
}
check(doubleNone, intQuestion, numQuestion);
check(numNone, doubleQuestion, numQuestion);
check(numNone, intQuestion, numQuestion);
}
test_none_star() {
void check(DartType T1, DartType T2, DartType expected) {
_assertNullabilityNone(T1);
_assertNullabilityStar(T2);
_assertNotSpecial(T1);
_assertNotSpecial(T2);
_checkLeastUpperBound(T1, T2, expected);
}
check(doubleNone, intStar, numStar);
check(numNone, doubleStar, numStar);
check(numNone, intStar, numStar);
}
test_null_any() {
void check(DartType T1, DartType T2, DartType expected) {
var T1_str = _typeString(T1);
var T2_str = _typeString(T2);
expect(typeSystem.isNull(T1), isTrue, reason: 'isNull: $T1_str');
expect(typeSystem.isNull(T2), isFalse, reason: 'isNull: $T2_str');
expect(typeSystem.isTop(T1), isFalse, reason: 'isTop: $T1_str');
expect(typeSystem.isTop(T2), isFalse, reason: 'isTop: $T2_str');
expect(typeSystem.isBottom(T1), isFalse, reason: 'isBottom: $T1_str');
expect(typeSystem.isBottom(T2), isFalse, reason: 'isBottom: $T2_str');
_checkLeastUpperBound(T1, T2, expected);
}
check(nullNone, objectNone, objectQuestion);
check(nullNone, intNone, intQuestion);
check(nullNone, intQuestion, intQuestion);
check(nullNone, intStar, intQuestion);
check(nullQuestion, intNone, intQuestion);
check(nullQuestion, intQuestion, intQuestion);
check(nullQuestion, intStar, intQuestion);
check(nullStar, intNone, intQuestion);
check(nullStar, intQuestion, intQuestion);
check(nullStar, intStar, intQuestion);
check(nullNone, listNone(intNone), listQuestion(intNone));
check(nullNone, listQuestion(intNone), listQuestion(intNone));
check(nullNone, listStar(intNone), listQuestion(intNone));
check(nullNone, futureOrNone(intNone), futureOrQuestion(intNone));
check(nullNone, futureOrQuestion(intNone), futureOrQuestion(intNone));
check(nullNone, futureOrStar(intNone), futureOrQuestion(intNone));
check(nullNone, futureOrNone(intQuestion), futureOrNone(intQuestion));
check(nullNone, futureOrStar(intQuestion), futureOrStar(intQuestion));
check(
nullNone,
functionTypeNone(returnType: intNone),
functionTypeQuestion(returnType: intNone),
);
}
test_null_null() {
void check(DartType T1, DartType T2) {
var T1_str = _typeString(T1);
var T2_str = _typeString(T2);
expect(typeSystem.isNull(T1), isTrue, reason: 'isNull: $T1_str');
expect(typeSystem.isNull(T2), isTrue, reason: 'isNull: $T2_str');
expect(typeSystem.isBottom(T1), isFalse, reason: 'isBottom: $T1_str');
expect(typeSystem.isBottom(T2), isFalse, reason: 'isBottom: $T2_str');
_checkLeastUpperBound(T1, T2, T2);
}
check(nullNone, nullQuestion);
check(nullNone, nullStar);
}
test_object_any() {
void check(DartType T1, DartType T2, DartType expected) {
var T1_str = _typeString(T1);
var T2_str = _typeString(T2);
expect(typeSystem.isObject(T1), isTrue, reason: 'isObject: $T1_str');
expect(typeSystem.isObject(T2), isFalse, reason: 'isObject: $T2_str');
_checkLeastUpperBound(T1, T2, expected);
}
check(objectNone, intNone, objectNone);
check(objectNone, intQuestion, objectQuestion);
check(objectNone, intStar, objectNone);
check(objectNone, futureOrNone(intQuestion), objectQuestion);
check(futureOrNone(objectNone), intNone, futureOrNone(objectNone));
check(futureOrNone(objectNone), intQuestion, futureOrQuestion(objectNone));
check(futureOrNone(objectNone), intStar, futureOrNone(objectNone));
}
test_object_object() {
void check(DartType T1, DartType T2) {
var T1_str = _typeString(T1);
var T2_str = _typeString(T2);
expect(typeSystem.isObject(T1), isTrue, reason: 'isObject: $T1_str');
expect(typeSystem.isObject(T2), isTrue, reason: 'isObject: $T2_str');
_checkLeastUpperBound(T1, T2, T2);
}
check(futureOrNone(objectNone), objectNone);
check(
futureOrNone(
futureOrNone(objectNone),
),
futureOrNone(objectNone),
);
}
test_question_question() {
void check(DartType T1, DartType T2, DartType expected) {
_assertNullabilityQuestion(T1);
_assertNullabilityQuestion(T2);
_assertNotSpecial(T1);
_assertNotSpecial(T2);
_checkLeastUpperBound(T1, T2, expected);
}
check(doubleQuestion, intQuestion, numQuestion);
check(numQuestion, doubleQuestion, numQuestion);
check(numQuestion, intQuestion, numQuestion);
}
test_question_star() {
void check(DartType T1, DartType T2, DartType expected) {
_assertNullabilityQuestion(T1);
_assertNullabilityStar(T2);
_assertNotSpecial(T1);
_assertNotSpecial(T2);
_checkLeastUpperBound(T1, T2, expected);
}
check(doubleQuestion, intStar, numQuestion);
check(numQuestion, doubleStar, numQuestion);
check(numQuestion, intStar, numQuestion);
}
test_star_star() {
void check(DartType T1, DartType T2, DartType expected) {
_assertNullabilityStar(T1);
_assertNullabilityStar(T2);
_assertNotSpecial(T1);
_assertNotSpecial(T2);
_checkLeastUpperBound(T1, T2, expected);
}
check(doubleStar, intStar, numStar);
check(numStar, doubleStar, numStar);
check(numStar, intStar, numStar);
}
test_top_any() {
void check(DartType T1, DartType T2) {
expect(typeSystem.isTop(T1), isTrue, reason: _typeString(T1));
expect(typeSystem.isTop(T2), isFalse, reason: _typeString(T2));
_checkLeastUpperBound(T1, T2, T1);
}
check(voidNone, objectNone);
check(voidNone, intNone);
check(voidNone, intQuestion);
check(voidNone, intStar);
check(voidNone, listNone(intNone));
check(voidNone, futureOrNone(intNone));
check(dynamicNone, objectNone);
check(dynamicNone, intNone);
check(dynamicNone, intQuestion);
check(dynamicNone, intStar);
check(dynamicNone, listNone(intNone));
check(dynamicNone, futureOrNone(intNone));
check(objectQuestion, objectNone);
check(objectQuestion, intNone);
check(objectQuestion, intQuestion);
check(objectQuestion, intStar);
check(objectQuestion, listNone(intNone));
check(objectQuestion, futureOrNone(intNone));
check(objectStar, objectNone);
check(objectStar, intNone);
check(objectStar, intQuestion);
check(objectStar, intStar);
check(objectStar, listNone(intNone));
check(objectStar, futureOrNone(intNone));
check(futureOrNone(voidNone), intNone);
check(futureOrQuestion(voidNone), intNone);
check(futureOrStar(voidNone), intNone);
}
test_top_top() {
void check(DartType T1, DartType T2) {
expect(typeSystem.isTop(T1), isTrue, reason: _typeString(T1));
expect(typeSystem.isTop(T2), isTrue, reason: _typeString(T2));
_checkLeastUpperBound(T1, T2, T1);
}
check(voidNone, dynamicNone);
check(voidNone, objectStar);
check(voidNone, objectQuestion);
check(voidNone, futureOrNone(voidNone));
check(voidNone, futureOrNone(dynamicNone));
check(voidNone, futureOrNone(objectQuestion));
check(voidNone, futureOrNone(objectStar));
check(dynamicNone, objectStar);
check(dynamicNone, objectQuestion);
check(dynamicNone, futureOrNone(voidNone));
check(dynamicNone, futureOrNone(dynamicNone));
check(dynamicNone, futureOrNone(objectQuestion));
check(dynamicNone, futureOrNone(objectStar));
check(
dynamicNone,
futureOrStar(objectStar),
);
check(objectQuestion, futureOrQuestion(voidNone));
check(objectQuestion, futureOrQuestion(dynamicNone));
check(objectQuestion, futureOrQuestion(objectNone));
check(objectQuestion, futureOrQuestion(objectQuestion));
check(objectQuestion, futureOrQuestion(objectStar));
check(objectQuestion, futureOrStar(voidNone));
check(objectQuestion, futureOrStar(dynamicNone));
check(objectQuestion, futureOrStar(objectNone));
check(objectQuestion, futureOrStar(objectQuestion));
check(objectQuestion, futureOrStar(objectStar));
check(objectStar, futureOrStar(voidNone));
check(objectStar, futureOrStar(dynamicNone));
check(objectStar, futureOrStar(objectNone));
check(objectStar, futureOrStar(objectQuestion));
check(objectStar, futureOrStar(objectStar));
check(futureOrNone(voidNone), objectQuestion);
check(futureOrNone(dynamicNone), objectQuestion);
check(futureOrNone(objectQuestion), objectQuestion);
check(futureOrNone(objectStar), objectQuestion);
check(futureOrNone(voidNone), futureOrNone(dynamicNone));
check(futureOrNone(voidNone), futureOrNone(objectQuestion));
check(futureOrNone(voidNone), futureOrNone(objectStar));
check(futureOrNone(dynamicNone), futureOrNone(objectQuestion));
check(futureOrNone(dynamicNone), futureOrNone(objectStar));
}
test_typeParameter_bound() {
void check(TypeParameterType T1, DartType T2, DartType expected) {
_assertNullabilityNone(T1);
_assertNullabilityNone(T2);
_assertNotSpecial(T1);
_assertNotSpecial(T2);
_checkLeastUpperBound(T1, T2, expected);
}
{
var T = typeParameter('T', bound: intNone);
check(typeParameterTypeNone(T), numNone, numNone);
}
{
var T = typeParameter('T', bound: intNone);
var U = typeParameter('U', bound: numNone);
check(typeParameterTypeNone(T), typeParameterTypeNone(U), numNone);
}
{
var T = typeParameter('T', bound: intNone);
var U = typeParameter('U', bound: numQuestion);
check(typeParameterTypeNone(T), typeParameterTypeNone(U), numQuestion);
}
{
var T = typeParameter('T', bound: intQuestion);
var U = typeParameter('U', bound: numNone);
check(typeParameterTypeNone(T), typeParameterTypeNone(U), numQuestion);
}
{
var T = typeParameter('T', bound: numNone);
var T_none = typeParameterTypeNone(T);
var U = typeParameter('U', bound: T_none);
check(T_none, typeParameterTypeNone(U), T_none);
}
}
void _assertNotBottom(DartType type) {
if (typeSystem.isBottom(type)) {
fail('isBottom must be false: ' + _typeString(type));
}
}
void _assertNotNull(DartType type) {
if (typeSystem.isNull(type)) {
fail('isNull must be false: ' + _typeString(type));
}
}
void _assertNotObject(DartType type) {
if (typeSystem.isObject(type)) {
fail('isObject must be false: ' + _typeString(type));
}
}
void _assertNotSpecial(DartType type) {
_assertNotBottom(type);
_assertNotNull(type);
_assertNotObject(type);
_assertNotTop(type);
}
void _assertNotTop(DartType type) {
if (typeSystem.isTop(type)) {
fail('isTop must be false: ' + _typeString(type));
}
}
void _assertNullability(DartType type, NullabilitySuffix expected) {
if ((type as TypeImpl).nullabilitySuffix != expected) {
fail('Expected $expected in ' + _typeString(type));
}
}
void _assertNullabilityNone(DartType type) {
_assertNullability(type, NullabilitySuffix.none);
}
void _assertNullabilityQuestion(DartType type) {
_assertNullability(type, NullabilitySuffix.question);
}
void _assertNullabilityStar(DartType type) {
_assertNullability(type, NullabilitySuffix.star);
}
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 _SubtypingTestBase with ElementsTypesMixin {
TypeProvider typeProvider;
TypeSystemImpl typeSystem;
FeatureSet get testFeatureSet {
return FeatureSet.forTesting();
}
void setUp() {
var analysisContext = TestAnalysisContext(
featureSet: testFeatureSet,
);
typeProvider = analysisContext.typeProvider;
typeSystem = analysisContext.typeSystem;
}
String _typeParametersStr(TypeImpl type) {
var typeStr = '';
var typeParameterCollector = _TypeParameterCollector();
DartTypeVisitor.visit(type, typeParameterCollector);
for (var typeParameter in typeParameterCollector.typeParameters) {
if (typeParameter is TypeParameterMember) {
var base = typeParameter.declaration;
var baseBound = base.bound as TypeImpl;
if (baseBound != null) {
var baseBoundStr = baseBound.toString(withNullability: true);
typeStr += ', ${typeParameter.name} extends ' + baseBoundStr;
}
var bound = typeParameter.bound as TypeImpl;
var boundStr = bound.toString(withNullability: true);
typeStr += ', ${typeParameter.name} & ' + boundStr;
} else {
var bound = typeParameter.bound as TypeImpl;
if (bound != null) {
var boundStr = bound.toString(withNullability: true);
typeStr += ', ${typeParameter.name} extends ' + boundStr;
}
}
}
return typeStr;
}
String _typeString(TypeImpl type) {
if (type == null) return null;
return type.toString(withNullability: true) + _typeParametersStr(type);
}
}
class _TypeParameterCollector extends DartTypeVisitor<void> {
final Set<TypeParameterElement> typeParameters = Set();
/// We don't need to print bounds for these type parameters, because
/// they are already included into the function type itself, and cannot
/// be promoted.
final Set<TypeParameterElement> functionTypeParameters = Set();
@override
void defaultDartType(DartType type) {
throw UnimplementedError('(${type.runtimeType}) $type');
}
@override
void visitDynamicType(DynamicTypeImpl type) {}
@override
void visitFunctionType(FunctionType type) {
functionTypeParameters.addAll(type.typeFormals);
for (var typeParameter in type.typeFormals) {
var bound = typeParameter.bound;
if (bound != null) {
DartTypeVisitor.visit(bound, this);
}
}
for (var parameter in type.parameters) {
DartTypeVisitor.visit(parameter.type, this);
}
DartTypeVisitor.visit(type.returnType, this);
}
@override
void visitInterfaceType(InterfaceType type) {
for (var typeArgument in type.typeArguments) {
DartTypeVisitor.visit(typeArgument, this);
}
}
@override
void visitNeverType(NeverTypeImpl type) {}
@override
void visitTypeParameterType(TypeParameterType type) {
if (!functionTypeParameters.contains(type.element)) {
typeParameters.add(type.element);
}
}
@override
void visitVoidType(VoidType type) {}
}