blob: 107c69634b78cb7307777b2f66752919319b7ace [file] [log] [blame]
// Copyright (c) 2022, 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/type_algebra.dart';
import '../ast.dart';
import 'legacy_erasure.dart';
import 'replacement_visitor.dart';
/// Replaces all occurrences of [TypedefType] in [type] with the corresponding
/// unaliased type.
///
/// If [legacyEraseAliases] is `true`, the unaliased types will be legacy
/// erased. This used when the [TypedefType] was used in a legacy library.
DartType unalias(DartType type, {required bool legacyEraseAliases}) {
return rawUnalias(type, legacyEraseAliases: legacyEraseAliases) ?? type;
}
/// Replaces all occurrences of [TypedefType] in [types] with the corresponding
/// unaliased types.
///
/// If [legacyEraseAliases] is `true`, the unaliased types will be legacy
/// erased. This used when the [TypedefType] was used in a legacy library.
List<DartType>? unaliasTypes(List<DartType>? types,
{required bool legacyEraseAliases}) {
if (types == null) return null;
return rawUnaliasTypes(types, legacyEraseAliases: legacyEraseAliases) ??
types;
}
/// Replaces all occurrences of [TypedefType] in [type] with the corresponding
/// unaliased type, or returns `null` if no [TypedefType]s were found.
///
/// If [legacyEraseAliases] is `true`, the unaliased types will be legacy
/// erased. This used when the [TypedefType] was used in a legacy library.
DartType? rawUnalias(DartType type, {required bool legacyEraseAliases}) {
return type.accept1(
legacyEraseAliases
? const _Unalias(legacyEraseAliases: true)
: const _Unalias(legacyEraseAliases: false),
Variance.covariant);
}
/// Replaces all occurrences of [TypedefType] in [types] with the corresponding
/// unaliased types, or returns `null` if no [TypedefType]s were found.
///
/// If [legacyEraseAliases] is `true`, the unaliased types will be legacy
/// erased. This used when the [TypedefType] was used in a legacy library.
List<DartType>? rawUnaliasTypes(List<DartType> types,
{required bool legacyEraseAliases}) {
List<DartType>? newTypes;
for (int i = 0; i < types.length; i++) {
DartType typeArgument = types[i];
DartType? newTypeArgument =
rawUnalias(typeArgument, legacyEraseAliases: legacyEraseAliases);
if (newTypeArgument != null) {
newTypes ??= types.toList(growable: false);
newTypes[i] = newTypeArgument;
}
}
return newTypes;
}
/// Visitor that replaces all occurrences of [TypedefType] with the
/// corresponding unaliased type, or returns `null` if no type was replaced.
///
/// If [legacyEraseAliases] is `true`, the unaliased types will be legacy
/// erased. This used when the [TypedefType] was used in a legacy library.
class _Unalias extends ReplacementVisitor {
final bool legacyEraseAliases;
const _Unalias({required this.legacyEraseAliases});
@override
DartType visitTypedefType(TypedefType node, int variance) {
DartType result;
if (node.typeArguments.isNotEmpty) {
List<DartType>? newTypeArguments = null;
for (int i = 0; i < node.typeArguments.length; i++) {
DartType? substitution = node.typeArguments[i].accept1(this, variance);
if (substitution != null) {
newTypeArguments ??= node.typeArguments.toList(growable: false);
newTypeArguments[i] = substitution;
}
}
if (newTypeArguments != null) {
result = new TypedefType(
node.typedefNode, node.nullability, newTypeArguments)
.unalias;
} else {
result = node.unalias;
}
} else {
result = node.unalias;
}
if (node.nullability == Nullability.legacy ||
node.typedefNode.type!.nullability == Nullability.legacy) {
// The typedef is defined or used in an opt-out library so the nullability
// is based on the use site alone.
result = result.withDeclaredNullability(node.nullability);
} else {
result = result.withDeclaredNullability(
uniteNullabilities(node.nullability, result.nullability));
}
if (legacyEraseAliases) {
result = legacyErasure(result);
}
return result;
}
}