blob: 9e4a88b4bae43cc7988a1c16dfbb44420f88d13b [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.md file.
import 'package:kernel/ast.dart';
import 'package:kernel/src/replacement_visitor.dart';
/// Returns `true` if type contains a promoted type variable.
bool hasPromotedTypeVariable(DartType type) {
return type.accept(const _HasPromotedTypeVariableVisitor());
}
/// Visitor that returns `true` if a type contains a promoted type variable.
class _HasPromotedTypeVariableVisitor extends DartTypeVisitor<bool> {
const _HasPromotedTypeVariableVisitor();
@override
bool defaultDartType(DartType node) => false;
@override
bool visitFunctionType(FunctionType node) {
if (node.returnType.accept(this)) return true;
for (DartType parameterType in node.positionalParameters) {
if (parameterType.accept(this)) return true;
}
for (NamedType namedParameterType in node.namedParameters) {
if (namedParameterType.type.accept(this)) return true;
}
TypedefType? typedefType = node.typedefType;
if (typedefType != null && typedefType.accept(this)) {
return true;
}
return false;
}
@override
bool visitInterfaceType(InterfaceType node) {
for (DartType typeArgument in node.typeArguments) {
if (typeArgument.accept(this)) return true;
}
return false;
}
@override
bool visitTypedefType(TypedefType node) {
for (DartType typeArgument in node.typeArguments) {
if (typeArgument.accept(this)) return true;
}
return false;
}
@override
bool visitTypeParameterType(TypeParameterType node) {
return node.promotedBound != null;
}
}
/// Returns [type] in which all promoted type variables have been replace with
/// their unpromoted equivalents, and where all nullabilities have been
/// normalized to the default nullability of [library].
///
/// If [library] is non-nullable by default all legacy types have been replaced
/// with non-nullable types. Otherwise all non-legacy types have been replaced
/// with legacy types.
DartType demoteTypeInLibrary(DartType type, Library library) {
if (library.isNonNullableByDefault) {
return type.accept1(
const _DemotionNullabilityNormalization(
demoteTypeVariables: true, forNonNullableByDefault: true),
Variance.covariant) ??
type;
} else {
return type.accept1(
const _DemotionNullabilityNormalization(
demoteTypeVariables: true, forNonNullableByDefault: false),
Variance.covariant) ??
type;
}
}
/// Returns [type] normalized to the known nullabilities of [library].
///
/// If [library] is non-nullable by default [type] returned (non-nullable
/// libraries can handle all kinds of nullability). Otherwise all
/// non-legacy types have been replaced with legacy types (legacy libraries
/// can only handle legacy types).
DartType normalizeNullabilityInLibrary(DartType type, Library library) {
if (library.isNonNullableByDefault) {
return type;
} else {
return type.accept1(
const _DemotionNullabilityNormalization(
demoteTypeVariables: false, forNonNullableByDefault: false),
Variance.covariant) ??
type;
}
}
/// Visitor that replaces all promoted type variables the type variable itself
/// and normalizes the type nullabilities.
///
/// The visitor returns `null` if the type wasn't changed.
class _DemotionNullabilityNormalization extends ReplacementVisitor {
final bool demoteTypeVariables;
final bool forNonNullableByDefault;
const _DemotionNullabilityNormalization(
{required this.demoteTypeVariables,
required this.forNonNullableByDefault})
// ignore: unnecessary_null_comparison
: assert(demoteTypeVariables != null),
// ignore: unnecessary_null_comparison
assert(forNonNullableByDefault != null);
@override
Nullability? visitNullability(DartType node) {
if (forNonNullableByDefault) {
if (node.declaredNullability == Nullability.legacy) {
return Nullability.nonNullable;
}
} else {
if (node.declaredNullability != Nullability.legacy) {
return Nullability.legacy;
}
}
return null;
}
@override
DartType? visitTypeParameterType(TypeParameterType node, int variance) {
Nullability? newNullability = visitNullability(node);
if (demoteTypeVariables && node.promotedBound != null) {
return new TypeParameterType(
node.parameter, newNullability ?? node.declaredNullability);
}
return createTypeParameterType(node, newNullability);
}
}