blob: 2ce91385ec02f2b7454b63ef5092c22d2e0246e4 [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 file.
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
class NotWellBoundedTypeResult implements TypeBoundedResult {
final String elementName;
final List<TypeArgumentIssue> issues;
NotWellBoundedTypeResult._({required this.elementName, required this.issues});
}
class RegularBoundedTypeResult implements WellBoundedTypeResult {
const RegularBoundedTypeResult._();
}
class SuperBoundedTypeResult implements WellBoundedTypeResult {
const SuperBoundedTypeResult._();
}
class TypeArgumentIssue {
/// The index for type argument within the passed type arguments.
final int index;
/// The type parameter with the bound that was violated.
final TypeParameterElement parameter;
/// The substituted bound of the [parameter].
final DartType parameterBound;
/// The type argument that violated the [parameterBound].
final DartType argument;
TypeArgumentIssue(
this.index,
this.parameter,
this.parameterBound,
this.argument,
);
@override
String toString() {
return 'TypeArgumentIssue(index=$index, parameter=$parameter, '
'parameterBound=$parameterBound, argument=$argument)';
}
}
/// Helper for checking whether a type if well-bounded.
///
/// See `15.2 Super-bounded types` in the language specification.
class TypeBoundedHelper {
final TypeSystemImpl typeSystem;
TypeBoundedHelper(this.typeSystem);
TypeBoundedResult isWellBounded(
TypeImpl type, {
required bool allowSuperBounded,
}) {
var result = _isRegularBounded(type);
if (!allowSuperBounded) {
return result;
}
return _isSuperBounded(type);
}
TypeBoundedResult _isRegularBounded(TypeImpl type) {
List<TypeArgumentIssue>? issues;
String? elementName;
List<TypeParameterElementImpl2> typeParameters;
List<TypeImpl> typeArguments;
var alias = type.alias;
if (alias != null) {
elementName = alias.element2.name3;
typeParameters = alias.element2.typeParameters2;
typeArguments = alias.typeArguments;
} else if (type is InterfaceTypeImpl) {
elementName = type.element3.name3;
typeParameters = type.element3.typeParameters2;
typeArguments = type.typeArguments;
} else {
return const RegularBoundedTypeResult._();
}
var substitution = Substitution.fromPairs2(typeParameters, typeArguments);
for (var i = 0; i < typeParameters.length; i++) {
var typeParameter = typeParameters[i];
var typeArgument = typeArguments[i];
var bound = typeParameter.bound;
if (bound == null) {
continue;
}
bound = substitution.substituteType(bound);
if (!typeSystem.isSubtypeOf(typeArgument, bound)) {
issues ??= <TypeArgumentIssue>[];
issues.add(TypeArgumentIssue(i, typeParameter, bound, typeArgument));
}
}
if (issues == null || elementName == null) {
return const RegularBoundedTypeResult._();
} else {
return NotWellBoundedTypeResult._(
elementName: elementName,
issues: issues,
);
}
}
TypeBoundedResult _isSuperBounded(TypeImpl type) {
var invertedType = typeSystem.replaceTopAndBottom(type);
var result = _isRegularBounded(invertedType);
if (result is RegularBoundedTypeResult) {
return const SuperBoundedTypeResult._();
} else {
return result;
}
}
}
abstract class TypeBoundedResult {}
class WellBoundedTypeResult implements TypeBoundedResult {}