blob: 5709733ad297c67c7c2cec1351997051b7d071ad [file] [log] [blame]
// 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.
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/extensions.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';
/// Replace every "top" type in a covariant position with [_bottomType].
/// Replace every "bottom" type in a contravariant position with [_topType].
class ReplaceTopBottomVisitor {
final TypeSystemImpl _typeSystem;
final DartType _topType;
final DartType _bottomType;
ReplaceTopBottomVisitor._(
this._typeSystem,
this._topType,
this._bottomType,
);
DartType process(DartType type, Variance variance) {
if (_typeSystem.isNonNullableByDefault) {
if (variance.isContravariant) {
// ...replacing every occurrence in `T` of a type `S` in a contravariant
// position where `S <: Never` by `Object?`
if (_typeSystem.isSubtypeOf(type, NeverTypeImpl.instance)) {
return _topType;
}
} else {
// ...and every occurrence in `T` of a top type in a position which
// is not contravariant by `Never`.
if (_typeSystem.isTop(type)) {
return _bottomType;
}
}
} else {
if (variance.isCovariant) {
// ...replacing every occurrence in `T` of a top type in a covariant
// position by `Null`
if (_typeSystem.isTop(type)) {
return _bottomType;
}
} else if (variance.isContravariant) {
// ...and every occurrence in `T` of `Null` in a contravariant
// position by `Object`
if (type.isDartCoreNull) {
return _topType;
}
}
}
if (type.aliasElement != null) {
return _typeAliasInstantiation(type, variance);
} else if (type is InterfaceType) {
return _interfaceType(type, variance);
} else if (type is FunctionType) {
return _functionType(type, variance);
}
return type;
}
DartType _functionType(FunctionType type, Variance variance) {
var newReturnType = process(type.returnType, variance);
var newParameters = type.parameters.map((parameter) {
return parameter.copyWith(
type: process(
parameter.type,
variance.combine(Variance.contravariant),
),
);
}).toList();
return FunctionTypeImpl(
typeFormals: type.typeFormals,
parameters: newParameters,
returnType: newReturnType,
nullabilitySuffix: type.nullabilitySuffix,
);
}
DartType _interfaceType(InterfaceType type, Variance variance) {
var typeParameters = type.element.typeParameters;
if (typeParameters.isEmpty) {
return type;
}
var typeArguments = type.typeArguments;
assert(typeParameters.length == typeArguments.length);
var newTypeArguments = <DartType>[];
for (var i = 0; i < typeArguments.length; i++) {
var newTypeArgument = process(typeArguments[i], variance);
newTypeArguments.add(newTypeArgument);
}
return InterfaceTypeImpl(
element: type.element,
nullabilitySuffix: type.nullabilitySuffix,
typeArguments: newTypeArguments,
);
}
DartType _typeAliasInstantiation(DartType type, Variance variance) {
var aliasElement = type.aliasElement!;
var aliasArguments = type.aliasArguments!;
var typeParameters = aliasElement.typeParameters;
assert(typeParameters.length == aliasArguments.length);
var newTypeArguments = <DartType>[];
for (var i = 0; i < typeParameters.length; i++) {
var typeParameter = typeParameters[i] as TypeParameterElementImpl;
newTypeArguments.add(
process(
aliasArguments[i],
typeParameter.variance.combine(variance),
),
);
}
return aliasElement.instantiate(
typeArguments: newTypeArguments,
nullabilitySuffix: type.nullabilitySuffix,
);
}
/// Runs an instance of the visitor on the given [type] and returns the
/// resulting type. If the type contains no instances of Top or Bottom, the
/// original type object is returned to avoid unnecessary allocation.
static DartType run({
required DartType topType,
required DartType bottomType,
required TypeSystemImpl typeSystem,
required DartType type,
}) {
var visitor = ReplaceTopBottomVisitor._(typeSystem, topType, bottomType);
return visitor.process(type, Variance.covariant);
}
}