| // 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.md file. |
| |
| import 'package:kernel/ast.dart' hide MapEntry; |
| import 'package:kernel/visitor.dart'; |
| |
| import '../type_inference/type_schema.dart'; |
| |
| /// Check if [type] contains [InvalidType] as its part. |
| /// |
| /// The helper function is intended for stopping cascading errors because of |
| /// occurrences of [InvalidType]. In most cases it's safe to assume that if an |
| /// [InvalidType] was produced, an error was reported as the part of its |
| /// creation, so other errors raising, for example, from inability to assign to |
| /// a variable of an [InvalidType], could be omitted. |
| bool containsInvalidType(DartType type) { |
| return type.accept1(const _InvalidTypeFinder(), <TypedefType>{}); |
| } |
| |
| class _InvalidTypeFinder implements DartTypeVisitor1<bool, Set<TypedefType>> { |
| const _InvalidTypeFinder(); |
| |
| @override |
| bool defaultDartType(DartType node, Set<TypedefType> visitedTypedefs) { |
| if (node is UnknownType) return false; |
| throw new StateError("Unhandled type ${node.runtimeType}."); |
| } |
| |
| @override |
| bool visitDynamicType(DynamicType node, Set<TypedefType> visitedTypedefs) => |
| false; |
| |
| @override |
| bool visitVoidType(VoidType node, Set<TypedefType> visitedTypedefs) => false; |
| |
| @override |
| bool visitInvalidType(InvalidType node, Set<TypedefType> visitedTypedefs) => |
| true; |
| |
| @override |
| bool visitInterfaceType( |
| InterfaceType node, Set<TypedefType> visitedTypedefs) { |
| for (DartType typeArgument in node.typeArguments) { |
| if (typeArgument.accept1(this, visitedTypedefs)) return true; |
| } |
| return false; |
| } |
| |
| @override |
| bool visitFutureOrType(FutureOrType node, Set<TypedefType> visitedTypedefs) { |
| return node.typeArgument.accept1(this, visitedTypedefs); |
| } |
| |
| @override |
| bool visitNeverType(NeverType node, Set<TypedefType> visitedTypedefs) => |
| false; |
| |
| @override |
| bool visitBottomType(BottomType node, Set<TypedefType> visitedTypedefs) => |
| false; |
| |
| @override |
| bool visitFunctionType(FunctionType node, Set<TypedefType> visitedTypedefs) { |
| if (node.returnType.accept1(this, visitedTypedefs)) return true; |
| for (TypeParameter typeParameter in node.typeParameters) { |
| if (typeParameter.bound.accept1(this, visitedTypedefs)) return true; |
| // TODO(dmitryas): Check defaultTypes as well if they cause cascading |
| // errors. |
| } |
| for (DartType parameter in node.positionalParameters) { |
| if (parameter.accept1(this, visitedTypedefs)) return true; |
| } |
| for (NamedType parameter in node.namedParameters) { |
| if (parameter.type.accept1(this, visitedTypedefs)) return true; |
| } |
| if (node.typedefType != null && visitedTypedefs.add(node.typedefType)) { |
| if (node.typedefType.accept1(this, visitedTypedefs)) return true; |
| } |
| return false; |
| } |
| |
| @override |
| bool visitTypedefType(TypedefType node, Set<TypedefType> visitedTypedefs) { |
| // The unaliased type should be checked, but it's faster to check the type |
| // arguments and the RHS separately than do the unaliasing because it avoids |
| // type substitution. |
| for (DartType typeArgument in node.typeArguments) { |
| if (typeArgument.accept1(this, visitedTypedefs)) return true; |
| } |
| if (node.typedefNode.type.accept1(this, visitedTypedefs)) return true; |
| return false; |
| } |
| |
| @override |
| bool visitTypeParameterType( |
| TypeParameterType node, Set<TypedefType> visitedTypedefs) { |
| // node.parameter.bound is not checked because such a bound doesn't |
| // automatically means that the potential errors related to the occurrences |
| // of the type-parameter type itself are reported. |
| if (node.promotedBound != null && |
| node.promotedBound.accept1(this, visitedTypedefs)) { |
| return true; |
| } |
| return false; |
| } |
| } |