mirror of
https://github.com/dart-lang/sdk
synced 2024-09-06 00:59:09 +00:00
[cfe] Use computeRawType in Null Safe UP and DOWN
Closes #43536. Bug: https://github.com/dart-lang/sdk/issues/43536 Change-Id: I6616739e7ac0eb0be084988cef0460e916a3b448 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/165804 Commit-Queue: Dmitry Stefantsov <dmitryas@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
parent
5a442b4f62
commit
3b4fe0dc06
|
@ -296,105 +296,6 @@ abstract class TypeConstraintGatherer {
|
|||
return type == coreTypes.nullType;
|
||||
}
|
||||
|
||||
/// Computes [type] as if declared without nullability markers.
|
||||
///
|
||||
/// The algorithm implemented by [_isNullabilityAwareSubtypeMatch] uses the
|
||||
/// notion of the nullable and the legacy type constructors, that is, int? and
|
||||
/// int* are applications of the nullable and the legacy type constructors to
|
||||
/// type int correspondingly. The algorithm requires the ability to peel off
|
||||
/// the two type constructors from the underlying types, which is done by
|
||||
/// [_computeRawType].
|
||||
DartType _computeRawType(DartType type) {
|
||||
if (type is TypeParameterType) {
|
||||
if (type.promotedBound == null) {
|
||||
// The default nullability for library is used when there are no
|
||||
// nullability markers on the type.
|
||||
return new TypeParameterType.withDefaultNullabilityForLibrary(
|
||||
type.parameter, _currentLibrary);
|
||||
} else {
|
||||
// Intersection types can't be arguments to the nullable and the legacy
|
||||
// type constructors, so nothing can be peeled off.
|
||||
return type;
|
||||
}
|
||||
} else if (type == coreTypes.nullType) {
|
||||
return type;
|
||||
} else {
|
||||
// For most types, peeling off the nullability constructors means that
|
||||
// they become non-nullable.
|
||||
return type.withDeclaredNullability(Nullability.nonNullable);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if [type] is declared without nullability markers.
|
||||
///
|
||||
/// The algorithm implemented by [_isNullabilityAwareSubtypeMatch] uses the
|
||||
/// notion of the nullable and the legacy type constructors, that is, int? and
|
||||
/// int* are applications of the nullable and the legacy type constructors to
|
||||
/// type int correspondingly. The algorithm requires the ability to detect if
|
||||
/// a type is raw, that is, if it is declared without any of the two
|
||||
/// constructors. Method [_isRawTypeParameterType] implements that check for
|
||||
/// [TypeParameterType]s. For most types, comparing their
|
||||
/// [DartType.declaredNullability] is enough, but the case of
|
||||
/// [TypeParameterType]s is more complex for two reasons: (1) intersection
|
||||
/// types are encoded as [TypeParameterType]s, and they can't be arguments of
|
||||
/// the nullability constructors, and (2) if a [TypeParameterType] is declared
|
||||
/// raw, its semantic nullability can be anything from
|
||||
/// [Nullability.nonNullable], [Nullability.undetermined], and
|
||||
/// [Nullability.legacy], depending on the bound. [_isRawTypeParameterType]
|
||||
/// checks if [type] has the same nullability as if it was declared without
|
||||
/// any nullability markers by the programmer.
|
||||
bool _isRawTypeParameterType(TypeParameterType type) {
|
||||
// The default nullability for library is used when there are no nullability
|
||||
// markers on the type.
|
||||
return type.promotedBound == null &&
|
||||
type.declaredNullability ==
|
||||
new TypeParameterType.withDefaultNullabilityForLibrary(
|
||||
type.parameter, _currentLibrary)
|
||||
.declaredNullability;
|
||||
}
|
||||
|
||||
/// Returns true if [type] is an application of the nullable type constructor.
|
||||
///
|
||||
/// The algorithm implemented by [_isNullabilityAwareSubtypeMatch] uses the
|
||||
/// notion of the nullable type constructor, that is, int? is an application
|
||||
/// of the nullable type constructor to type int. The algorithm requires the
|
||||
/// ability to detect if a type is an application of the nullable constructor
|
||||
/// to another type, which is done by [_isNullableTypeConstructorApplication].
|
||||
bool _isNullableTypeConstructorApplication(DartType type) {
|
||||
return type.declaredNullability == Nullability.nullable &&
|
||||
type is! DynamicType &&
|
||||
type is! VoidType &&
|
||||
type != coreTypes.nullType;
|
||||
}
|
||||
|
||||
/// Returns true if [type] is an application of the legacy type constructor.
|
||||
///
|
||||
/// The algorithm implemented by [_isNullabilityAwareSubtypeMatch] uses the
|
||||
/// notion of the legacy type constructor, that is, int* is an application of
|
||||
/// the legacy type constructor to type int. The algorithm requires the
|
||||
/// ability to detect if a type is an application of the nullable constructor
|
||||
/// to another type, which is done by [_isNullableTypeConstructorApplication].
|
||||
bool _isLegacyTypeConstructorApplication(DartType type) {
|
||||
if (type is TypeParameterType) {
|
||||
if (type.promotedBound == null) {
|
||||
// The legacy nullability is considered an application of the legacy
|
||||
// nullability constructor if it doesn't match the default nullability
|
||||
// of the type-parameter type for the library.
|
||||
return type.declaredNullability == Nullability.legacy &&
|
||||
type.declaredNullability !=
|
||||
new TypeParameterType.withDefaultNullabilityForLibrary(
|
||||
type.parameter, _currentLibrary)
|
||||
.declaredNullability;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (type is InvalidType) {
|
||||
return false;
|
||||
} else {
|
||||
return type.declaredNullability == Nullability.legacy;
|
||||
}
|
||||
}
|
||||
|
||||
/// Matches [p] against [q] as a subtype against supertype.
|
||||
///
|
||||
/// Returns true if [p] is a subtype of [q] under some constraints, and false
|
||||
|
@ -473,7 +374,7 @@ abstract class TypeConstraintGatherer {
|
|||
//
|
||||
// Under constraint _ <: X <: Q.
|
||||
if (p is TypeParameterType &&
|
||||
_isRawTypeParameterType(p) &&
|
||||
isTypeParameterTypeWithoutNullabilityMarker(p, _currentLibrary) &&
|
||||
_parametersToConstrain.contains(p.parameter)) {
|
||||
_constrainParameterUpper(p.parameter, q);
|
||||
return true;
|
||||
|
@ -483,7 +384,7 @@ abstract class TypeConstraintGatherer {
|
|||
//
|
||||
// Under constraint P <: X <: _.
|
||||
if (q is TypeParameterType &&
|
||||
_isRawTypeParameterType(q) &&
|
||||
isTypeParameterTypeWithoutNullabilityMarker(q, _currentLibrary) &&
|
||||
_parametersToConstrain.contains(q.parameter)) {
|
||||
_constrainParameterLower(q.parameter, p);
|
||||
return true;
|
||||
|
@ -502,8 +403,11 @@ abstract class TypeConstraintGatherer {
|
|||
// If P is a legacy type P0* then the match holds under constraint set C:
|
||||
//
|
||||
// Only if P0 is a subtype match for Q under constraint set C.
|
||||
if (_isLegacyTypeConstructorApplication(p)) {
|
||||
return _isNullabilityAwareSubtypeMatch(_computeRawType(p), q,
|
||||
if (isLegacyTypeConstructorApplication(p, _currentLibrary)) {
|
||||
return _isNullabilityAwareSubtypeMatch(
|
||||
computeTypeWithoutNullabilityMarker(p, _currentLibrary,
|
||||
nullType: coreTypes.nullType),
|
||||
q,
|
||||
constrainSupertype: constrainSupertype);
|
||||
}
|
||||
|
||||
|
@ -513,11 +417,14 @@ abstract class TypeConstraintGatherer {
|
|||
// set C.
|
||||
// Or if P is not dynamic or void and P is a subtype match for Q0? under
|
||||
// constraint set C.
|
||||
if (_isLegacyTypeConstructorApplication(q)) {
|
||||
if (isLegacyTypeConstructorApplication(q, _currentLibrary)) {
|
||||
final int baseConstraintCount = _protoConstraints.length;
|
||||
|
||||
if ((p is DynamicType || p is VoidType) &&
|
||||
_isNullabilityAwareSubtypeMatch(p, _computeRawType(q),
|
||||
_isNullabilityAwareSubtypeMatch(
|
||||
p,
|
||||
computeTypeWithoutNullabilityMarker(q, _currentLibrary,
|
||||
nullType: coreTypes.nullType),
|
||||
constrainSupertype: constrainSupertype)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -580,12 +487,17 @@ abstract class TypeConstraintGatherer {
|
|||
// Or if P is a subtype match for Q0 under non-empty constraint set C.
|
||||
// Or if P is a subtype match for Null under constraint set C.
|
||||
// Or if P is a subtype match for Q0 under empty constraint set C.
|
||||
if (_isNullableTypeConstructorApplication(q)) {
|
||||
if (isNullableTypeConstructorApplication(q, nullType: coreTypes.nullType)) {
|
||||
final int baseConstraintCount = _protoConstraints.length;
|
||||
final DartType rawP = _computeRawType(p);
|
||||
final DartType rawQ = _computeRawType(q);
|
||||
final DartType rawP = computeTypeWithoutNullabilityMarker(
|
||||
p, _currentLibrary,
|
||||
nullType: coreTypes.nullType);
|
||||
final DartType rawQ = computeTypeWithoutNullabilityMarker(
|
||||
q, _currentLibrary,
|
||||
nullType: coreTypes.nullType);
|
||||
|
||||
if (_isNullableTypeConstructorApplication(p) &&
|
||||
if (isNullableTypeConstructorApplication(p,
|
||||
nullType: coreTypes.nullType) &&
|
||||
_isNullabilityAwareSubtypeMatch(rawP, rawQ,
|
||||
constrainSupertype: constrainSupertype)) {
|
||||
return true;
|
||||
|
@ -641,9 +553,12 @@ abstract class TypeConstraintGatherer {
|
|||
//
|
||||
// If P0 is a subtype match for Q under constraint set C1.
|
||||
// And if Null is a subtype match for Q under constraint set C2.
|
||||
if (_isNullableTypeConstructorApplication(p)) {
|
||||
if (isNullableTypeConstructorApplication(p, nullType: coreTypes.nullType)) {
|
||||
final int baseConstraintCount = _protoConstraints.length;
|
||||
if (_isNullabilityAwareSubtypeMatch(_computeRawType(p), q,
|
||||
if (_isNullabilityAwareSubtypeMatch(
|
||||
computeTypeWithoutNullabilityMarker(p, _currentLibrary,
|
||||
nullType: coreTypes.nullType),
|
||||
q,
|
||||
constrainSupertype: constrainSupertype) &&
|
||||
_isNullabilityAwareSubtypeMatch(coreTypes.nullType, q,
|
||||
constrainSupertype: constrainSupertype)) {
|
||||
|
|
|
@ -1069,6 +1069,7 @@ executed
|
|||
executes
|
||||
executing
|
||||
execution
|
||||
exempt
|
||||
exercise
|
||||
exhaustive
|
||||
exist
|
||||
|
@ -2130,6 +2131,7 @@ peek
|
|||
peel
|
||||
peeled
|
||||
peeling
|
||||
peels
|
||||
pending
|
||||
percent
|
||||
percolate
|
||||
|
|
13
pkg/front_end/testcases/nnbd/issue43536.dart
Normal file
13
pkg/front_end/testcases/nnbd/issue43536.dart
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
class C<T> {
|
||||
foo<E extends T>(List<E> list) {
|
||||
List<E> variable = method(list);
|
||||
}
|
||||
|
||||
List<F> method<F extends T>(List<F> list) => list;
|
||||
}
|
||||
|
||||
main() {}
|
14
pkg/front_end/testcases/nnbd/issue43536.dart.outline.expect
Normal file
14
pkg/front_end/testcases/nnbd/issue43536.dart.outline.expect
Normal file
|
@ -0,0 +1,14 @@
|
|||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class C<T extends core::Object? = dynamic> extends core::Object {
|
||||
synthetic constructor •() → self::C<self::C::T%>
|
||||
;
|
||||
method foo<generic-covariant-impl E extends self::C::T% = self::C::T%>(core::List<self::C::foo::E%> list) → dynamic
|
||||
;
|
||||
method method<generic-covariant-impl F extends self::C::T% = self::C::T%>(core::List<self::C::method::F%> list) → core::List<self::C::method::F%>
|
||||
;
|
||||
}
|
||||
static method main() → dynamic
|
||||
;
|
15
pkg/front_end/testcases/nnbd/issue43536.dart.strong.expect
Normal file
15
pkg/front_end/testcases/nnbd/issue43536.dart.strong.expect
Normal file
|
@ -0,0 +1,15 @@
|
|||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class C<T extends core::Object? = dynamic> extends core::Object {
|
||||
synthetic constructor •() → self::C<self::C::T%>
|
||||
: super core::Object::•()
|
||||
;
|
||||
method foo<generic-covariant-impl E extends self::C::T% = self::C::T%>(core::List<self::C::foo::E%> list) → dynamic {
|
||||
core::List<self::C::foo::E%> variable = this.{self::C::method}<self::C::foo::E%>(list);
|
||||
}
|
||||
method method<generic-covariant-impl F extends self::C::T% = self::C::T%>(core::List<self::C::method::F%> list) → core::List<self::C::method::F%>
|
||||
return list;
|
||||
}
|
||||
static method main() → dynamic {}
|
|
@ -0,0 +1,15 @@
|
|||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class C<T extends core::Object? = dynamic> extends core::Object {
|
||||
synthetic constructor •() → self::C<self::C::T%>
|
||||
: super core::Object::•()
|
||||
;
|
||||
method foo<generic-covariant-impl E extends self::C::T% = self::C::T%>(core::List<self::C::foo::E%> list) → dynamic {
|
||||
core::List<self::C::foo::E%> variable = this.{self::C::method}<self::C::foo::E%>(list);
|
||||
}
|
||||
method method<generic-covariant-impl F extends self::C::T% = self::C::T%>(core::List<self::C::method::F%> list) → core::List<self::C::method::F%>
|
||||
return list;
|
||||
}
|
||||
static method main() → dynamic {}
|
|
@ -0,0 +1,6 @@
|
|||
class C<T> {
|
||||
foo<E extends T>(List<E> list) {}
|
||||
List<F> method<F extends T>(List<F> list) => list;
|
||||
}
|
||||
|
||||
main() {}
|
|
@ -0,0 +1,6 @@
|
|||
class C<T> {
|
||||
List<F> method<F extends T>(List<F> list) => list;
|
||||
foo<E extends T>(List<E> list) {}
|
||||
}
|
||||
|
||||
main() {}
|
15
pkg/front_end/testcases/nnbd/issue43536.dart.weak.expect
Normal file
15
pkg/front_end/testcases/nnbd/issue43536.dart.weak.expect
Normal file
|
@ -0,0 +1,15 @@
|
|||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class C<T extends core::Object? = dynamic> extends core::Object {
|
||||
synthetic constructor •() → self::C<self::C::T%>
|
||||
: super core::Object::•()
|
||||
;
|
||||
method foo<generic-covariant-impl E extends self::C::T% = self::C::T%>(core::List<self::C::foo::E%> list) → dynamic {
|
||||
core::List<self::C::foo::E%> variable = this.{self::C::method}<self::C::foo::E%>(list);
|
||||
}
|
||||
method method<generic-covariant-impl F extends self::C::T% = self::C::T%>(core::List<self::C::method::F%> list) → core::List<self::C::method::F%>
|
||||
return list;
|
||||
}
|
||||
static method main() → dynamic {}
|
|
@ -0,0 +1,15 @@
|
|||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class C<T extends core::Object? = dynamic> extends core::Object {
|
||||
synthetic constructor •() → self::C<self::C::T%>
|
||||
: super core::Object::•()
|
||||
;
|
||||
method foo<generic-covariant-impl E extends self::C::T% = self::C::T%>(core::List<self::C::foo::E%> list) → dynamic {
|
||||
core::List<self::C::foo::E%> variable = this.{self::C::method}<self::C::foo::E%>(list);
|
||||
}
|
||||
method method<generic-covariant-impl F extends self::C::T% = self::C::T%>(core::List<self::C::method::F%> list) → core::List<self::C::method::F%>
|
||||
return list;
|
||||
}
|
||||
static method main() → dynamic {}
|
|
@ -396,17 +396,19 @@ mixin StandardBounds {
|
|||
// without using the nullability of the outermost type. The result uses
|
||||
// [intersectNullabilities] to compute the resulting type if the subtype
|
||||
// relation is established.
|
||||
DartType nonNullableType1 =
|
||||
type1.withDeclaredNullability(Nullability.nonNullable);
|
||||
DartType nonNullableType2 =
|
||||
type2.withDeclaredNullability(Nullability.nonNullable);
|
||||
if (isSubtypeOf(nonNullableType1, nonNullableType2,
|
||||
SubtypeCheckMode.withNullabilities)) {
|
||||
DartType typeWithoutNullabilityMarker1 =
|
||||
computeTypeWithoutNullabilityMarker(type1, clientLibrary,
|
||||
nullType: coreTypes.nullType);
|
||||
DartType typeWithoutNullabilityMarker2 =
|
||||
computeTypeWithoutNullabilityMarker(type2, clientLibrary,
|
||||
nullType: coreTypes.nullType);
|
||||
if (isSubtypeOf(typeWithoutNullabilityMarker1,
|
||||
typeWithoutNullabilityMarker2, SubtypeCheckMode.withNullabilities)) {
|
||||
return type1.withDeclaredNullability(intersectNullabilities(
|
||||
type1.declaredNullability, type2.declaredNullability));
|
||||
}
|
||||
if (isSubtypeOf(nonNullableType2, nonNullableType1,
|
||||
SubtypeCheckMode.withNullabilities)) {
|
||||
if (isSubtypeOf(typeWithoutNullabilityMarker2,
|
||||
typeWithoutNullabilityMarker1, SubtypeCheckMode.withNullabilities)) {
|
||||
return type2.withDeclaredNullability(intersectNullabilities(
|
||||
type1.declaredNullability, type2.declaredNullability));
|
||||
}
|
||||
|
@ -753,21 +755,23 @@ mixin StandardBounds {
|
|||
// T1 <: T2 without using the nullability of the outermost type. The result
|
||||
// uses [uniteNullabilities] to compute the resulting type if the subtype
|
||||
// relation is established.
|
||||
InterfaceType nonNonNullableType1 =
|
||||
type1.withDeclaredNullability(Nullability.nonNullable);
|
||||
InterfaceType nonNonNullableType2 =
|
||||
type2.withDeclaredNullability(Nullability.nonNullable);
|
||||
InterfaceType typeWithoutNullabilityMarker1 =
|
||||
computeTypeWithoutNullabilityMarker(type1, clientLibrary,
|
||||
nullType: coreTypes.nullType);
|
||||
InterfaceType typeWithoutNullabilityMarker2 =
|
||||
computeTypeWithoutNullabilityMarker(type2, clientLibrary,
|
||||
nullType: coreTypes.nullType);
|
||||
|
||||
if (isSubtypeOf(nonNonNullableType1, nonNonNullableType2,
|
||||
SubtypeCheckMode.withNullabilities)) {
|
||||
if (isSubtypeOf(typeWithoutNullabilityMarker1,
|
||||
typeWithoutNullabilityMarker2, SubtypeCheckMode.withNullabilities)) {
|
||||
return type2.withDeclaredNullability(
|
||||
uniteNullabilities(type1.nullability, type2.nullability));
|
||||
}
|
||||
|
||||
// UP(T1, T2) = T1 if T2 <: T1
|
||||
// Note that both types must be class types at this point.
|
||||
if (isSubtypeOf(nonNonNullableType2, nonNonNullableType1,
|
||||
SubtypeCheckMode.withNullabilities)) {
|
||||
if (isSubtypeOf(typeWithoutNullabilityMarker2,
|
||||
typeWithoutNullabilityMarker1, SubtypeCheckMode.withNullabilities)) {
|
||||
return type1.withDeclaredNullability(uniteNullabilities(
|
||||
type1.declaredNullability, type2.declaredNullability));
|
||||
}
|
||||
|
|
|
@ -1035,16 +1035,18 @@ DartType unwrapNullabilityConstructor(DartType type, CoreTypes coreTypes) {
|
|||
|
||||
/// Implementation of [unwrapNullabilityConstructor] as a visitor.
|
||||
///
|
||||
/// Implementing the function as a visitor makes the necessity of supporting a new implementation of [DartType] visible at compile time.
|
||||
// TODO(dmitryas): Remove CoreTypes as the second argument when NullType is landed.
|
||||
/// Implementing the function as a visitor makes the necessity of supporting a
|
||||
/// new implementation of [DartType] visible at compile time.
|
||||
// TODO(dmitryas): Remove CoreTypes as the second argument when NullType is
|
||||
// landed.
|
||||
class _NullabilityConstructorUnwrapper
|
||||
implements DartTypeVisitor1<DartType, CoreTypes> {
|
||||
const _NullabilityConstructorUnwrapper();
|
||||
|
||||
@override
|
||||
DartType defaultDartType(DartType node, CoreTypes coreTypes) {
|
||||
throw new UnsupportedError(
|
||||
"Unsupported operation: _NullabilityConstructorUnwrapper(${node.runtimeType})");
|
||||
throw new UnsupportedError("Unsupported operation: "
|
||||
"_NullabilityConstructorUnwrapper(${node.runtimeType})");
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -1185,3 +1187,93 @@ class NullabilityAwareTypeVariableEliminator extends ReplacementVisitor {
|
|||
return super.visitTypeParameterType(node);
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes [type] as if declared without nullability markers.
|
||||
///
|
||||
/// For example, int? and int* are considered applications of the nullable and
|
||||
/// the legacy type constructors to type int correspondingly.
|
||||
/// [computeTypeWithoutNullabilityMarker] peels off these type constructors,
|
||||
/// returning the non-nullable version of type int. In case of
|
||||
/// [TypeParameterType]s, the result may be either [Nullability.nonNullable] or
|
||||
/// [Nullability.undetermined], depending on the bound.
|
||||
DartType computeTypeWithoutNullabilityMarker(
|
||||
DartType type, Library clientLibrary,
|
||||
{DartType nullType}) {
|
||||
assert(nullType != null);
|
||||
if (type is TypeParameterType) {
|
||||
if (type.promotedBound == null) {
|
||||
// The default nullability for library is used when there are no
|
||||
// nullability markers on the type.
|
||||
return new TypeParameterType.withDefaultNullabilityForLibrary(
|
||||
type.parameter, clientLibrary);
|
||||
} else {
|
||||
// Intersection types can't be arguments to the nullable and the legacy
|
||||
// type constructors, so nothing can be peeled off.
|
||||
return type;
|
||||
}
|
||||
} else if (type == nullType) {
|
||||
return type;
|
||||
} else {
|
||||
// For most types, peeling off the nullability constructors means that
|
||||
// they become non-nullable.
|
||||
return type.withDeclaredNullability(Nullability.nonNullable);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if [type] is declared without nullability markers.
|
||||
///
|
||||
/// An example of the nullable type constructor application is T? where T is a
|
||||
/// type parameter. Some examples of types declared without nullability markers
|
||||
/// are T% and S, where T and S are type parameters such that T extends Object?
|
||||
/// and S extends Object.
|
||||
bool isTypeParameterTypeWithoutNullabilityMarker(
|
||||
TypeParameterType type, Library clientLibrary) {
|
||||
// The default nullability for library is used when there are no nullability
|
||||
// markers on the type.
|
||||
return type.promotedBound == null &&
|
||||
type.declaredNullability ==
|
||||
new TypeParameterType.withDefaultNullabilityForLibrary(
|
||||
type.parameter, clientLibrary)
|
||||
.declaredNullability;
|
||||
}
|
||||
|
||||
/// Returns true if [type] is an application of the nullable type constructor.
|
||||
///
|
||||
/// A type is considered an application of the nullable type constructor if it
|
||||
/// was declared with the ? marker. Some examples of such types are int?,
|
||||
/// String?, Object?, and T? where T is a type parameter. Types dynamic, void,
|
||||
/// and Null are nullable, but aren't considered applications of the nullable
|
||||
/// type constructor.
|
||||
bool isNullableTypeConstructorApplication(DartType type, {DartType nullType}) {
|
||||
assert(nullType != null);
|
||||
return type.declaredNullability == Nullability.nullable &&
|
||||
type is! DynamicType &&
|
||||
type is! VoidType &&
|
||||
type != nullType;
|
||||
}
|
||||
|
||||
/// Returns true if [type] is an application of the legacy type constructor.
|
||||
///
|
||||
/// A type is considered an application of the legacy type constructor if it was
|
||||
/// declared within a legacy library and is not one of exempt types, such as
|
||||
/// dynamic or void.
|
||||
bool isLegacyTypeConstructorApplication(DartType type, Library clientLibrary) {
|
||||
if (type is TypeParameterType) {
|
||||
if (type.promotedBound == null) {
|
||||
// The legacy nullability is considered an application of the legacy
|
||||
// nullability constructor if it doesn't match the default nullability
|
||||
// of the type-parameter type for the library.
|
||||
return type.declaredNullability == Nullability.legacy &&
|
||||
type.declaredNullability !=
|
||||
new TypeParameterType.withDefaultNullabilityForLibrary(
|
||||
type.parameter, clientLibrary)
|
||||
.declaredNullability;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (type is InvalidType) {
|
||||
return false;
|
||||
} else {
|
||||
return type.declaredNullability == Nullability.legacy;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue