| Index: tests/compiler/dart2js/related_types.dart
|
| diff --git a/tests/compiler/dart2js/related_types.dart b/tests/compiler/dart2js/related_types.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..299b97ab70c827364c95688818f4208ba9695e39
|
| --- /dev/null
|
| +++ b/tests/compiler/dart2js/related_types.dart
|
| @@ -0,0 +1,413 @@
|
| +// Copyright (c) 2015, 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.
|
| +
|
| +library related_types;
|
| +
|
| +import 'package:compiler/src/compiler.dart';
|
| +import 'package:compiler/src/core_types.dart';
|
| +import 'package:compiler/src/dart_types.dart';
|
| +import 'package:compiler/src/diagnostics/messages.dart';
|
| +import 'package:compiler/src/elements/elements.dart';
|
| +import 'package:compiler/src/resolution/semantic_visitor.dart';
|
| +import 'package:compiler/src/tree/tree.dart';
|
| +import 'package:compiler/src/universe/universe.dart';
|
| +import 'package:compiler/src/world.dart';
|
| +
|
| +/// Check all loaded libraries in [compiler] for unrelated types.
|
| +void checkRelatedTypes(Compiler compiler) {
|
| + for (LibraryElement library in compiler.libraryLoader.libraries) {
|
| + checkLibraryElement(compiler, library);
|
| + }
|
| +}
|
| +
|
| +/// Check [library] for unrelated types.
|
| +void checkLibraryElement(Compiler compiler, LibraryElement library) {
|
| + library.forEachLocalMember((Element element) {
|
| + if (element.isClass) {
|
| + ClassElement cls = element;
|
| + cls.forEachLocalMember((MemberElement member) {
|
| + checkMemberElement(compiler, member);
|
| + });
|
| + } else if (!element.isTypedef) {
|
| + checkMemberElement(compiler, element);
|
| + }
|
| + });
|
| +}
|
| +
|
| +/// Check [member] for unrelated types.
|
| +void checkMemberElement(Compiler compiler, MemberElement member) {
|
| + if (!compiler.enqueuer.resolution.hasBeenResolved(member)) return;
|
| +
|
| + ResolvedAst resolvedAst = member.resolvedAst;
|
| + RelatedTypesChecker relatedTypesChecker =
|
| + new RelatedTypesChecker(compiler, resolvedAst);
|
| + if (resolvedAst.node != null) {
|
| + compiler.withCurrentElement(member.implementation, () {
|
| + relatedTypesChecker.apply(resolvedAst.node);
|
| + });
|
| + }
|
| +}
|
| +
|
| +class RelatedTypesChecker extends TraversalVisitor<DartType, dynamic> {
|
| + final Compiler compiler;
|
| + final ResolvedAst resolvedAst;
|
| +
|
| + RelatedTypesChecker(this.compiler, ResolvedAst resolvedAst)
|
| + : this.resolvedAst = resolvedAst,
|
| + super(resolvedAst.elements);
|
| +
|
| + ClassWorld get world => compiler.world;
|
| +
|
| + CoreTypes get coreTypes => compiler.coreTypes;
|
| +
|
| + InterfaceType get thisType => resolvedAst.element.enclosingClass.thisType;
|
| +
|
| + /// Returns `true` if there exists no common subtype of [left] and [right].
|
| + bool hasEmptyIntersection(DartType left, DartType right) {
|
| + if (left == right) return false;
|
| + if (left == null || right == null) return false;
|
| + ClassElement leftClass = const ClassFinder().findClass(left);
|
| + ClassElement rightClass = const ClassFinder().findClass(right);
|
| + if (leftClass != null && rightClass != null) {
|
| + return !world.haveAnyCommonSubtypes(leftClass, rightClass);
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + /// Checks that there exists a common subtype of [left] and [right] or report
|
| + /// a hint otherwise.
|
| + void checkRelated(Node node, DartType left, DartType right) {
|
| + if (hasEmptyIntersection(left, right)) {
|
| + compiler.reportHint(
|
| + node, MessageKind.NO_COMMON_SUBTYPES, {'left': left, 'right': right});
|
| + }
|
| + }
|
| +
|
| + /// Check weakly typed collection methods, like `Map.containsKey`,
|
| + /// `Map.containsValue` and `Iterable.contains`.
|
| + void checkDynamicInvoke(
|
| + Node node,
|
| + DartType receiverType,
|
| + List<DartType> argumentTypes,
|
| + Selector selector) {
|
| + if (selector.name == 'containsKey' &&
|
| + selector.callStructure == CallStructure.ONE_ARG) {
|
| + InterfaceType mapType = findMapType(receiverType);
|
| + if (mapType != null) {
|
| + DartType keyType = findMapKeyType(mapType);
|
| + checkRelated(node, keyType, argumentTypes.first);
|
| + }
|
| + } else if (selector.name == 'containsValue' &&
|
| + selector.callStructure == CallStructure.ONE_ARG) {
|
| + InterfaceType mapType = findMapType(receiverType);
|
| + if (mapType != null) {
|
| + DartType valueType = findMapValueType(mapType);
|
| + checkRelated(node, valueType, argumentTypes.first);
|
| + }
|
| + } else if (selector.name == 'contains' &&
|
| + selector.callStructure == CallStructure.ONE_ARG) {
|
| + InterfaceType iterableType = findIterableType(receiverType);
|
| + if (iterableType != null) {
|
| + DartType elementType = findIterableElementType(iterableType);
|
| + checkRelated(node, elementType, argumentTypes.first);
|
| + }
|
| + } else if (selector.name == 'remove' &&
|
| + selector.callStructure == CallStructure.ONE_ARG) {
|
| + InterfaceType mapType = findMapType(receiverType);
|
| + if (mapType != null) {
|
| + DartType keyType = findMapKeyType(mapType);
|
| + checkRelated(node, keyType, argumentTypes.first);
|
| + }
|
| + InterfaceType listType = findListType(receiverType);
|
| + if (listType != null) {
|
| + DartType valueType = findListElementType(listType);
|
| + checkRelated(node, valueType, argumentTypes.first);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /// Return the interface type implemented by [type] or `null` if no interface
|
| + /// type is implied by [type].
|
| + InterfaceType findInterfaceType(DartType type) {
|
| + return Types.computeInterfaceType(compiler, type);
|
| + }
|
| +
|
| + /// Returns the supertype of [receiver] that implements [cls], if any.
|
| + InterfaceType findClassType(DartType receiver, ClassElement cls) {
|
| + InterfaceType interfaceType = findInterfaceType(receiver);
|
| + if (interfaceType == null) return null;
|
| + InterfaceType mapType = interfaceType.asInstanceOf(cls);
|
| + if (mapType == null) return null;
|
| + return mapType;
|
| + }
|
| +
|
| + /// Returns the supertype of [receiver] that implements `Iterable`, if any.
|
| + InterfaceType findIterableType(DartType receiver) {
|
| + return findClassType(receiver, compiler.iterableClass);
|
| + }
|
| +
|
| + /// Returns the element type of the supertype of [receiver] that implements
|
| + /// `Iterable`, if any.
|
| + DartType findIterableElementType(InterfaceType iterableType) {
|
| + if (iterableType == null) return null;
|
| + return iterableType.typeArguments[0];
|
| + }
|
| +
|
| + /// Returns the supertype of [receiver] that implements `Map`, if any.
|
| + InterfaceType findMapType(DartType receiver) {
|
| + return findClassType(receiver, compiler.mapClass);
|
| + }
|
| +
|
| + /// Returns the key type of the supertype of [receiver] that implements
|
| + /// `Map`, if any.
|
| + DartType findMapKeyType(InterfaceType mapType) {
|
| + if (mapType == null) return null;
|
| + return mapType.typeArguments[0];
|
| + }
|
| +
|
| + /// Returns the value type of the supertype of [receiver] that implements
|
| + /// `Map`, if any.
|
| + DartType findMapValueType(InterfaceType mapType) {
|
| + if (mapType == null) return null;
|
| + return mapType.typeArguments[1];
|
| + }
|
| +
|
| + /// Returns the supertype of [receiver] that implements `List`, if any.
|
| + InterfaceType findListType(DartType receiver) {
|
| + return findClassType(receiver, compiler.listClass);
|
| + }
|
| +
|
| + /// Returns the element type of the supertype of [receiver] that implements
|
| + /// `List`, if any.
|
| + DartType findListElementType(InterfaceType listType) {
|
| + if (listType == null) return null;
|
| + return listType.typeArguments[0];
|
| + }
|
| +
|
| + /// Returns the implied return type of [type] or `dynamic` if no return type
|
| + /// is implied.
|
| + DartType findReturnType(DartType type) {
|
| + if (type is FunctionType) {
|
| + return type.returnType;
|
| + }
|
| + return const DynamicType();
|
| + }
|
| +
|
| + /// Visits [arguments] and returns the list of their corresponding types.
|
| + List<DartType> findArgumentTypes(NodeList arguments) {
|
| + List<DartType> argumentTypes = <DartType>[];
|
| + for (Node argument in arguments) {
|
| + argumentTypes.add(apply(argument));
|
| + }
|
| + return argumentTypes;
|
| + }
|
| +
|
| + /// Finds the [MemberSignature] of the [name] property on [type], if any.
|
| + MemberSignature lookupInterfaceMember(DartType type, Name name) {
|
| + InterfaceType interfaceType = findInterfaceType(type);
|
| + if (interfaceType == null) return null;
|
| + return interfaceType.lookupInterfaceMember(name);
|
| + }
|
| +
|
| + /// Returns the type of an access of the [name] property on [type], or
|
| + /// `dynamic` if no property was found.
|
| + DartType lookupInterfaceMemberAccessType(DartType type, Name name) {
|
| + MemberSignature member = lookupInterfaceMember(type, name);
|
| + if (member == null) return const DynamicType();
|
| + return member.type;
|
| + }
|
| +
|
| + /// Returns the function type of the [name] property on [type], or
|
| + /// `dynamic` if no property was found.
|
| + FunctionType lookupInterfaceMemberInvocationType(DartType type, Name name) {
|
| + MemberSignature member = lookupInterfaceMember(type, name);
|
| + if (member == null) return null;
|
| + return member.functionType;
|
| + }
|
| +
|
| + DartType apply(Node node, [_]) {
|
| + DartType type = node.accept(this);
|
| + if (type == null) {
|
| + type = const DynamicType();
|
| + }
|
| + return type;
|
| + }
|
| +
|
| + @override
|
| + DartType visitEquals(Send node, Node left, Node right, _) {
|
| + DartType leftType = apply(left);
|
| + DartType rightType = apply(right);
|
| + checkRelated(node, leftType, rightType);
|
| + return coreTypes.boolType;
|
| + }
|
| +
|
| + @override
|
| + DartType visitNotEquals(Send node, Node left, Node right, _) {
|
| + DartType leftType = apply(left);
|
| + DartType rightType = apply(right);
|
| + checkRelated(node, leftType, rightType);
|
| + return coreTypes.boolType;
|
| + }
|
| +
|
| + @override
|
| + DartType visitIndex(Send node, Node receiver, Node index, _) {
|
| + DartType receiverType = apply(receiver);
|
| + DartType indexType = apply(index);
|
| + InterfaceType mapType = findMapType(receiverType);
|
| + DartType keyType = findMapKeyType(mapType);
|
| + DartType valueType = findMapValueType(mapType);
|
| + checkRelated(index, keyType, indexType);
|
| + return valueType;
|
| + }
|
| +
|
| + @override
|
| + DartType visitLiteralInt(LiteralInt node) {
|
| + return coreTypes.intType;
|
| + }
|
| +
|
| + @override
|
| + DartType visitLiteralString(LiteralString node) {
|
| + return coreTypes.stringType;
|
| + }
|
| +
|
| + @override
|
| + DartType visitLiteralBool(LiteralBool node) {
|
| + return coreTypes.boolType;
|
| + }
|
| +
|
| + @override
|
| + DartType visitLiteralMap(LiteralMap node) {
|
| + return elements.getType(node);
|
| + }
|
| +
|
| + @override
|
| + DartType visitLiteralList(LiteralList node) {
|
| + return elements.getType(node);
|
| + }
|
| +
|
| + @override
|
| + DartType visitLiteralNull(LiteralNull node) {
|
| + return elements.getType(node);
|
| + }
|
| +
|
| + @override
|
| + DartType visitLocalVariableGet(Send node, LocalVariableElement variable, _) {
|
| + return variable.type;
|
| + }
|
| +
|
| + @override
|
| + DartType visitLocalFunctionGet(Send node, LocalFunctionElement function, _) {
|
| + return function.type;
|
| + }
|
| +
|
| + @override
|
| + DartType visitParameterGet(Send node, ParameterElement parameter, _) {
|
| + return parameter.type;
|
| + }
|
| +
|
| + @override
|
| + DartType visitThisPropertyGet(Send node, Name name, _) {
|
| + return lookupInterfaceMemberAccessType(thisType, name);
|
| + }
|
| +
|
| + @override
|
| + DartType visitDynamicPropertyGet(Send node, Node receiver, Name name, _) {
|
| + DartType receiverType = apply(receiver);
|
| + return lookupInterfaceMemberAccessType(receiverType, name);
|
| + }
|
| +
|
| + @override
|
| + DartType visitIfNotNullDynamicPropertyGet(
|
| + Send node, Node receiver, Name name, _) {
|
| + DartType receiverType = apply(receiver);
|
| + return lookupInterfaceMemberAccessType(receiverType, name);
|
| + }
|
| +
|
| + @override
|
| + DartType visitStaticFieldGet(Send node, FieldElement field, _) {
|
| + return field.type;
|
| + }
|
| +
|
| + @override
|
| + DartType visitTopLevelFieldGet(Send node, FieldElement field, _) {
|
| + return field.type;
|
| + }
|
| +
|
| + @override
|
| + DartType visitDynamicPropertyInvoke(
|
| + Send node,
|
| + Node receiver,
|
| + NodeList arguments,
|
| + Selector selector, _) {
|
| + DartType receiverType = apply(receiver);
|
| + List<DartType> argumentTypes = findArgumentTypes(arguments);
|
| + FunctionType methodType = lookupInterfaceMemberInvocationType(
|
| + receiverType, selector.memberName);
|
| + checkDynamicInvoke(node, receiverType, argumentTypes, selector);
|
| + return findReturnType(methodType);
|
| + }
|
| +
|
| + @override
|
| + DartType visitThisPropertyInvoke(
|
| + Send node,
|
| + NodeList arguments,
|
| + Selector selector, _) {
|
| + DartType receiverType = thisType;
|
| + List<DartType> argumentTypes = findArgumentTypes(arguments);
|
| + FunctionType methodType = lookupInterfaceMemberInvocationType(
|
| + receiverType, selector.memberName);
|
| + checkDynamicInvoke(node, receiverType, argumentTypes, selector);
|
| + return findReturnType(methodType);
|
| + }
|
| +
|
| + @override
|
| + DartType visitIfNotNullDynamicPropertyInvoke(
|
| + Send node,
|
| + Node receiver,
|
| + NodeList arguments,
|
| + Selector selector, _) {
|
| + DartType receiverType = apply(receiver);
|
| + List<DartType> argumentTypes = findArgumentTypes(arguments);
|
| + FunctionType methodType = lookupInterfaceMemberInvocationType(
|
| + receiverType, selector.memberName);
|
| + checkDynamicInvoke(node, receiverType, argumentTypes, selector);
|
| + return findReturnType(methodType);
|
| + }
|
| +
|
| + @override
|
| + DartType visitTopLevelFunctionInvoke(
|
| + Send node,
|
| + MethodElement function,
|
| + NodeList arguments,
|
| + CallStructure callStructure, _) {
|
| + apply(arguments);
|
| + return findReturnType(function.type);
|
| + }
|
| +
|
| + @override
|
| + DartType visitStaticFunctionInvoke(
|
| + Send node,
|
| + MethodElement function,
|
| + NodeList arguments,
|
| + CallStructure callStructure, _) {
|
| + apply(arguments);
|
| + return findReturnType(function.type);
|
| + }
|
| +}
|
| +
|
| +/// Computes the [ClassElement] implied by a type.
|
| +// TODO(johnniwinther): Handle type variables, function types and typedefs.
|
| +class ClassFinder extends BaseDartTypeVisitor<ClassElement, dynamic> {
|
| + const ClassFinder();
|
| +
|
| + ClassElement findClass(DartType type) => type.accept(this, null);
|
| +
|
| + @override
|
| + ClassElement visitType(DartType type, _) => null;
|
| +
|
| + @override
|
| + ClassElement visitInterfaceType(InterfaceType type, _) {
|
| + return type.element;
|
| + }
|
| +}
|
|
|