| Index: pkg/analyzer/lib/src/generated/resolver.dart
|
| diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
|
| index 9849653671f387880a6ba0cb621a650dd1ebf06a..8d75e759bffa7ed79b4dabe01e127a8de9e5b1fc 100644
|
| --- a/pkg/analyzer/lib/src/generated/resolver.dart
|
| +++ b/pkg/analyzer/lib/src/generated/resolver.dart
|
| @@ -44,6 +44,10 @@ typedef TypeResolverVisitor TypeResolverVisitorFactory(
|
|
|
| typedef void VoidFunction();
|
|
|
| +typedef bool _GuardedSubtypeChecker<T>(T t1, T t2, Set<Element> visited);
|
| +
|
| +typedef bool _SubtypeChecker<T>(T t1, T t2);
|
| +
|
| /**
|
| * Instances of the class `BestPracticesVerifier` traverse an AST structure looking for
|
| * violations of Dart best practices.
|
| @@ -2511,6 +2515,7 @@ class ElementBuilder extends RecursiveAstVisitor<Object> {
|
| //
|
| constructors = _createDefaultConstructors(interfaceType);
|
| }
|
| + _setDocRange(element, node);
|
| element.abstract = node.isAbstract;
|
| element.accessors = holder.accessors;
|
| element.constructors = constructors;
|
| @@ -2584,6 +2589,7 @@ class ElementBuilder extends RecursiveAstVisitor<Object> {
|
| SimpleIdentifier constructorName = node.name;
|
| ConstructorElementImpl element =
|
| new ConstructorElementImpl.forNode(constructorName);
|
| + _setDocRange(element, node);
|
| if (node.externalKeyword != null) {
|
| element.external = true;
|
| }
|
| @@ -2688,6 +2694,7 @@ class ElementBuilder extends RecursiveAstVisitor<Object> {
|
| SimpleIdentifier enumName = node.name;
|
| ClassElementImpl enumElement = new ClassElementImpl.forNode(enumName);
|
| enumElement.enum2 = true;
|
| + _setDocRange(enumElement, node);
|
| InterfaceTypeImpl enumType = new InterfaceTypeImpl(enumElement);
|
| enumElement.type = enumType;
|
| // The equivalent code for enums in the spec shows a single constructor,
|
| @@ -2760,6 +2767,7 @@ class ElementBuilder extends RecursiveAstVisitor<Object> {
|
| SimpleIdentifier functionName = node.name;
|
| FunctionElementImpl element =
|
| new FunctionElementImpl.forNode(functionName);
|
| + _setDocRange(element, node);
|
| if (node.externalKeyword != null) {
|
| element.external = true;
|
| }
|
| @@ -2806,6 +2814,7 @@ class ElementBuilder extends RecursiveAstVisitor<Object> {
|
| if (node.isGetter) {
|
| PropertyAccessorElementImpl getter =
|
| new PropertyAccessorElementImpl.forNode(propertyNameNode);
|
| + _setDocRange(getter, node);
|
| if (node.externalKeyword != null) {
|
| getter.external = true;
|
| }
|
| @@ -2831,6 +2840,7 @@ class ElementBuilder extends RecursiveAstVisitor<Object> {
|
| } else {
|
| PropertyAccessorElementImpl setter =
|
| new PropertyAccessorElementImpl.forNode(propertyNameNode);
|
| + _setDocRange(setter, node);
|
| if (node.externalKeyword != null) {
|
| setter.external = true;
|
| }
|
| @@ -2917,6 +2927,7 @@ class ElementBuilder extends RecursiveAstVisitor<Object> {
|
| List<TypeParameterElement> typeParameters = holder.typeParameters;
|
| FunctionTypeAliasElementImpl element =
|
| new FunctionTypeAliasElementImpl.forNode(aliasName);
|
| + _setDocRange(element, node);
|
| element.parameters = parameters;
|
| element.typeParameters = typeParameters;
|
| FunctionTypeImpl type = new FunctionTypeImpl.forTypedef(element);
|
| @@ -2988,6 +2999,7 @@ class ElementBuilder extends RecursiveAstVisitor<Object> {
|
| }
|
| MethodElementImpl element =
|
| new MethodElementImpl(nameOfMethod, methodName.offset);
|
| + _setDocRange(element, node);
|
| element.abstract = node.isAbstract;
|
| if (node.externalKeyword != null) {
|
| element.external = true;
|
| @@ -3024,6 +3036,7 @@ class ElementBuilder extends RecursiveAstVisitor<Object> {
|
| if (node.isGetter) {
|
| PropertyAccessorElementImpl getter =
|
| new PropertyAccessorElementImpl.forNode(propertyNameNode);
|
| + _setDocRange(getter, node);
|
| if (node.externalKeyword != null) {
|
| getter.external = true;
|
| }
|
| @@ -3049,6 +3062,7 @@ class ElementBuilder extends RecursiveAstVisitor<Object> {
|
| } else {
|
| PropertyAccessorElementImpl setter =
|
| new PropertyAccessorElementImpl.forNode(propertyNameNode);
|
| + _setDocRange(setter, node);
|
| if (node.externalKeyword != null) {
|
| setter.external = true;
|
| }
|
| @@ -3185,6 +3199,9 @@ class ElementBuilder extends RecursiveAstVisitor<Object> {
|
| field = new FieldElementImpl.forNode(fieldName);
|
| }
|
| element = field;
|
| + if (node.parent.parent is FieldDeclaration) {
|
| + _setDocRange(element, node.parent.parent);
|
| + }
|
| if ((node.parent as VariableDeclarationList).type == null) {
|
| field.hasImplicitType = true;
|
| }
|
| @@ -3350,6 +3367,17 @@ class ElementBuilder extends RecursiveAstVisitor<Object> {
|
| }
|
|
|
| /**
|
| + * If the given [node] has a documentation comment, remember its range
|
| + * into the given [element].
|
| + */
|
| + void _setDocRange(ElementImpl element, AnnotatedNode node) {
|
| + Comment comment = node.documentationComment;
|
| + if (comment != null && comment.isDocumentation) {
|
| + element.setDocRange(comment.offset, comment.length);
|
| + }
|
| + }
|
| +
|
| + /**
|
| * Sets the visible source range for formal parameter.
|
| */
|
| void _setParameterVisibleRange(
|
| @@ -12720,268 +12748,572 @@ abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
|
| }
|
|
|
| /**
|
| - * Instances of this class manage the knowledge of what the set of subtypes are for a given type.
|
| + * Implementation of [TypeSystem] using the strong mode rules.
|
| + * https://github.com/dart-lang/dev_compiler/blob/master/STRONG_MODE.md
|
| */
|
| -class SubtypeManager {
|
| - /**
|
| - * A map between [ClassElement]s and a set of [ClassElement]s that are subtypes of the
|
| - * key.
|
| - */
|
| - HashMap<ClassElement, HashSet<ClassElement>> _subtypeMap =
|
| - new HashMap<ClassElement, HashSet<ClassElement>>();
|
| -
|
| - /**
|
| - * The set of all [LibraryElement]s that have been visited by the manager. This is used both
|
| - * to prevent infinite loops in the recursive methods, and also as a marker for the scope of the
|
| - * libraries visited by this manager.
|
| - */
|
| - HashSet<LibraryElement> _visitedLibraries = new HashSet<LibraryElement>();
|
| +class StrongTypeSystemImpl implements TypeSystem {
|
| + final _specTypeSystem = new TypeSystemImpl();
|
|
|
| - /**
|
| - * Given some [ClassElement], return the set of all subtypes, and subtypes of subtypes.
|
| - *
|
| - * @param classElement the class to recursively return the set of subtypes of
|
| - */
|
| - HashSet<ClassElement> computeAllSubtypes(ClassElement classElement) {
|
| - // Ensure that we have generated the subtype map for the library
|
| - _computeSubtypesInLibrary(classElement.library);
|
| - // use the subtypeMap to compute the set of all subtypes and subtype's
|
| - // subtypes
|
| - HashSet<ClassElement> allSubtypes = new HashSet<ClassElement>();
|
| - _safelyComputeAllSubtypes(
|
| - classElement, new HashSet<ClassElement>(), allSubtypes);
|
| - return allSubtypes;
|
| - }
|
| + StrongTypeSystemImpl();
|
|
|
| - /**
|
| - * Given some [LibraryElement], visit all of the types in the library, the passed library,
|
| - * and any imported libraries, will be in the [visitedLibraries] set.
|
| - *
|
| - * @param libraryElement the library to visit, it it hasn't been visited already
|
| - */
|
| - void ensureLibraryVisited(LibraryElement libraryElement) {
|
| - _computeSubtypesInLibrary(libraryElement);
|
| + @override
|
| + DartType getLeastUpperBound(
|
| + TypeProvider typeProvider, DartType type1, DartType type2) {
|
| + // TODO(leafp): Implement a strong mode version of this.
|
| + return _specTypeSystem.getLeastUpperBound(typeProvider, type1, type2);
|
| }
|
|
|
| - /**
|
| - * Given some [ClassElement], this method adds all of the pairs combinations of itself and
|
| - * all of its supertypes to the [subtypeMap] map.
|
| - *
|
| - * @param classElement the class element
|
| - */
|
| - void _computeSubtypesInClass(ClassElement classElement) {
|
| - InterfaceType supertypeType = classElement.supertype;
|
| - if (supertypeType != null) {
|
| - ClassElement supertypeElement = supertypeType.element;
|
| - if (supertypeElement != null) {
|
| - _putInSubtypeMap(supertypeElement, classElement);
|
| - }
|
| + // TODO(leafp): Document the rules in play here
|
| + @override
|
| + bool isAssignableTo(DartType fromType, DartType toType) {
|
| + // An actual subtype
|
| + if (isSubtypeOf(fromType, toType)) {
|
| + return true;
|
| }
|
| - List<InterfaceType> interfaceTypes = classElement.interfaces;
|
| - for (InterfaceType interfaceType in interfaceTypes) {
|
| - ClassElement interfaceElement = interfaceType.element;
|
| - if (interfaceElement != null) {
|
| - _putInSubtypeMap(interfaceElement, classElement);
|
| - }
|
| +
|
| + // Don't allow implicit downcasts between function types
|
| + // and call method objects, as these will almost always fail.
|
| + if ((fromType is FunctionType && _getCallMethodType(toType) != null) ||
|
| + (toType is FunctionType && _getCallMethodType(fromType) != null)) {
|
| + return false;
|
| }
|
| - List<InterfaceType> mixinTypes = classElement.mixins;
|
| - for (InterfaceType mixinType in mixinTypes) {
|
| - ClassElement mixinElement = mixinType.element;
|
| - if (mixinElement != null) {
|
| - _putInSubtypeMap(mixinElement, classElement);
|
| - }
|
| +
|
| + // If the subtype relation goes the other way, allow the implicit downcast.
|
| + // TODO(leafp): Emit warnings and hints for these in some way.
|
| + // TODO(leafp): Consider adding a flag to disable these? Or just rely on
|
| + // --warnings-as-errors?
|
| + if (isSubtypeOf(toType, fromType) ||
|
| + _specTypeSystem.isAssignableTo(toType, fromType)) {
|
| + // TODO(leafp): error if type is known to be exact (literal,
|
| + // instance creation).
|
| + // TODO(leafp): Warn on composite downcast.
|
| + // TODO(leafp): hint on object/dynamic downcast.
|
| + // TODO(leafp): Consider allowing assignment casts.
|
| + return true;
|
| }
|
| +
|
| + return false;
|
| }
|
|
|
| - /**
|
| - * Given some [CompilationUnitElement], this method calls
|
| - * [computeAllSubtypes] on all of the [ClassElement]s in the
|
| - * compilation unit.
|
| - *
|
| - * @param unitElement the compilation unit element
|
| - */
|
| - void _computeSubtypesInCompilationUnit(CompilationUnitElement unitElement) {
|
| - List<ClassElement> classElements = unitElement.types;
|
| - for (ClassElement classElement in classElements) {
|
| - _computeSubtypesInClass(classElement);
|
| - }
|
| + @override
|
| + bool isSubtypeOf(DartType leftType, DartType rightType) {
|
| + return _isSubtypeOf(leftType, rightType, null);
|
| }
|
|
|
| - /**
|
| - * Given some [LibraryElement], this method calls
|
| - * [computeAllSubtypes] on all of the [ClassElement]s in the
|
| - * compilation unit, and itself for all imported and exported libraries. All visited libraries are
|
| - * added to the [visitedLibraries] set.
|
| - *
|
| - * @param libraryElement the library element
|
| - */
|
| - void _computeSubtypesInLibrary(LibraryElement libraryElement) {
|
| - if (libraryElement == null || _visitedLibraries.contains(libraryElement)) {
|
| - return;
|
| - }
|
| - _visitedLibraries.add(libraryElement);
|
| - _computeSubtypesInCompilationUnit(libraryElement.definingCompilationUnit);
|
| - List<CompilationUnitElement> parts = libraryElement.parts;
|
| - for (CompilationUnitElement part in parts) {
|
| - _computeSubtypesInCompilationUnit(part);
|
| - }
|
| - List<LibraryElement> imports = libraryElement.importedLibraries;
|
| - for (LibraryElement importElt in imports) {
|
| - _computeSubtypesInLibrary(importElt.library);
|
| - }
|
| - List<LibraryElement> exports = libraryElement.exportedLibraries;
|
| - for (LibraryElement exportElt in exports) {
|
| - _computeSubtypesInLibrary(exportElt.library);
|
| + FunctionType _getCallMethodType(DartType t) {
|
| + if (t is InterfaceType) {
|
| + ClassElement element = t.element;
|
| + InheritanceManager manager = new InheritanceManager(element.library);
|
| + FunctionType callType = manager.lookupMemberType(t, "call");
|
| + return callType;
|
| }
|
| + return null;
|
| }
|
|
|
| - /**
|
| - * Add some key/ value pair into the [subtypeMap] map.
|
| - *
|
| - * @param supertypeElement the key for the [subtypeMap] map
|
| - * @param subtypeElement the value for the [subtypeMap] map
|
| - */
|
| - void _putInSubtypeMap(
|
| - ClassElement supertypeElement, ClassElement subtypeElement) {
|
| - HashSet<ClassElement> subtypes = _subtypeMap[supertypeElement];
|
| - if (subtypes == null) {
|
| - subtypes = new HashSet<ClassElement>();
|
| - _subtypeMap[supertypeElement] = subtypes;
|
| - }
|
| - subtypes.add(subtypeElement);
|
| + // Given a type t, if t is an interface type with a call method
|
| + // defined, return the function type for the call method, otherwise
|
| + // return null.
|
| + _GuardedSubtypeChecker<DartType> _guard(
|
| + _GuardedSubtypeChecker<DartType> check) {
|
| + return (DartType t1, DartType t2, Set<Element> visited) {
|
| + Element element = t1.element;
|
| + if (visited == null) {
|
| + visited = new HashSet<Element>();
|
| + }
|
| + if (element == null || !visited.add(element)) {
|
| + return false;
|
| + }
|
| + try {
|
| + return check(t1, t2, visited);
|
| + } finally {
|
| + visited.remove(element);
|
| + }
|
| + };
|
| + }
|
| +
|
| + bool _isBottom(DartType t, {bool dynamicIsBottom: false}) {
|
| + return (t.isDynamic && dynamicIsBottom) || t.isBottom;
|
| }
|
|
|
| + // Guard against loops in the class hierarchy
|
| /**
|
| - * Given some [ClassElement] and a [HashSet<ClassElement>], this method recursively
|
| - * adds all of the subtypes of the [ClassElement] to the passed array.
|
| - *
|
| - * @param classElement the type to compute the set of subtypes of
|
| - * @param visitedClasses the set of class elements that this method has already recursively seen
|
| - * @param allSubtypes the computed set of subtypes of the passed class element
|
| + * Check that [f1] is a subtype of [f2].
|
| + * [fuzzyArrows] indicates whether or not the f1 and f2 should be
|
| + * treated as fuzzy arrow types (and hence dynamic parameters to f2 treated
|
| + * as bottom).
|
| */
|
| - void _safelyComputeAllSubtypes(ClassElement classElement,
|
| - HashSet<ClassElement> visitedClasses, HashSet<ClassElement> allSubtypes) {
|
| - if (!visitedClasses.add(classElement)) {
|
| - // if this class has already been called on this class element
|
| - return;
|
| + bool _isFunctionSubtypeOf(FunctionType f1, FunctionType f2,
|
| + {bool fuzzyArrows: true}) {
|
| + final r1s = f1.normalParameterTypes;
|
| + final o1s = f1.optionalParameterTypes;
|
| + final n1s = f1.namedParameterTypes;
|
| + final r2s = f2.normalParameterTypes;
|
| + final o2s = f2.optionalParameterTypes;
|
| + final n2s = f2.namedParameterTypes;
|
| + final ret1 = f1.returnType;
|
| + final ret2 = f2.returnType;
|
| +
|
| + // A -> B <: C -> D if C <: A and
|
| + // either D is void or B <: D
|
| + if (!ret2.isVoid && !isSubtypeOf(ret1, ret2)) {
|
| + return false;
|
| }
|
| - HashSet<ClassElement> subtypes = _subtypeMap[classElement];
|
| - if (subtypes == null) {
|
| - return;
|
| +
|
| + // Reject if one has named and the other has optional
|
| + if (n1s.length > 0 && o2s.length > 0) {
|
| + return false;
|
| }
|
| - for (ClassElement subtype in subtypes) {
|
| - _safelyComputeAllSubtypes(subtype, visitedClasses, allSubtypes);
|
| + if (n2s.length > 0 && o1s.length > 0) {
|
| + return false;
|
| }
|
| - allSubtypes.addAll(subtypes);
|
| - }
|
| -}
|
|
|
| -/**
|
| - * Instances of the class `ToDoFinder` find to-do comments in Dart code.
|
| - */
|
| -class ToDoFinder {
|
| - /**
|
| - * The error reporter by which to-do comments will be reported.
|
| - */
|
| - final ErrorReporter _errorReporter;
|
| + // Rebind _isSubtypeOf for convenience
|
| + _SubtypeChecker<DartType> parameterSubtype = (DartType t1, DartType t2) =>
|
| + _isSubtypeOf(t1, t2, null, dynamicIsBottom: fuzzyArrows);
|
|
|
| - /**
|
| - * Initialize a newly created to-do finder to report to-do comments to the given reporter.
|
| - *
|
| - * @param errorReporter the error reporter by which to-do comments will be reported
|
| - */
|
| - ToDoFinder(this._errorReporter);
|
| + // f2 has named parameters
|
| + if (n2s.length > 0) {
|
| + // Check that every named parameter in f2 has a match in f1
|
| + for (String k2 in n2s.keys) {
|
| + if (!n1s.containsKey(k2)) {
|
| + return false;
|
| + }
|
| + if (!parameterSubtype(n2s[k2], n1s[k2])) {
|
| + return false;
|
| + }
|
| + }
|
| + }
|
| + // If we get here, we either have no named parameters,
|
| + // or else the named parameters match and we have no optional
|
| + // parameters
|
|
|
| - /**
|
| - * Search the comments in the given compilation unit for to-do comments and report an error for
|
| - * each.
|
| - *
|
| - * @param unit the compilation unit containing the to-do comments
|
| - */
|
| - void findIn(CompilationUnit unit) {
|
| - _gatherTodoComments(unit.beginToken);
|
| + // If f1 has more required parameters, reject
|
| + if (r1s.length > r2s.length) {
|
| + return false;
|
| + }
|
| +
|
| + // If f2 has more required + optional parameters, reject
|
| + if (r2s.length + o2s.length > r1s.length + o1s.length) {
|
| + return false;
|
| + }
|
| +
|
| + // The parameter lists must look like the following at this point
|
| + // where rrr is a region of required, and ooo is a region of optionals.
|
| + // f1: rrr ooo ooo ooo
|
| + // f2: rrr rrr ooo
|
| + int rr = r1s.length; // required in both
|
| + int or = r2s.length - r1s.length; // optional in f1, required in f2
|
| + int oo = o2s.length; // optional in both
|
| +
|
| + for (int i = 0; i < rr; ++i) {
|
| + if (!parameterSubtype(r2s[i], r1s[i])) {
|
| + return false;
|
| + }
|
| + }
|
| + for (int i = 0, j = rr; i < or; ++i, ++j) {
|
| + if (!parameterSubtype(r2s[j], o1s[i])) {
|
| + return false;
|
| + }
|
| + }
|
| + for (int i = or, j = 0; i < oo; ++i, ++j) {
|
| + if (!parameterSubtype(o2s[j], o1s[i])) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| }
|
|
|
| - /**
|
| - * Search the comment tokens reachable from the given token and create errors for each to-do
|
| - * comment.
|
| - *
|
| - * @param token the head of the list of tokens being searched
|
| - */
|
| - void _gatherTodoComments(sc.Token token) {
|
| - while (token != null && token.type != sc.TokenType.EOF) {
|
| - sc.Token commentToken = token.precedingComments;
|
| - while (commentToken != null) {
|
| - if (commentToken.type == sc.TokenType.SINGLE_LINE_COMMENT ||
|
| - commentToken.type == sc.TokenType.MULTI_LINE_COMMENT) {
|
| - _scrapeTodoComment(commentToken);
|
| + bool _isInterfaceSubtypeOf(
|
| + InterfaceType i1, InterfaceType i2, Set<Element> visited) {
|
| + // Guard recursive calls
|
| + _GuardedSubtypeChecker<InterfaceType> guardedInterfaceSubtype =
|
| + _guard(_isInterfaceSubtypeOf);
|
| +
|
| + if (i1 == i2) {
|
| + return true;
|
| + }
|
| +
|
| + if (i1.element == i2.element) {
|
| + List<DartType> tArgs1 = i1.typeArguments;
|
| + List<DartType> tArgs2 = i2.typeArguments;
|
| +
|
| + assert(tArgs1.length == tArgs2.length);
|
| +
|
| + for (int i = 0; i < tArgs1.length; i++) {
|
| + DartType t1 = tArgs1[i];
|
| + DartType t2 = tArgs2[i];
|
| + if (!isSubtypeOf(t1, t2)) {
|
| + return false;
|
| }
|
| - commentToken = commentToken.next;
|
| }
|
| - token = token.next;
|
| + return true;
|
| + }
|
| +
|
| + if (i2.isDartCoreFunction && i1.element.getMethod("call") != null) {
|
| + return true;
|
| + }
|
| +
|
| + if (i1.isObject) {
|
| + return false;
|
| + }
|
| +
|
| + if (guardedInterfaceSubtype(i1.superclass, i2, visited)) {
|
| + return true;
|
| + }
|
| +
|
| + for (final parent in i1.interfaces) {
|
| + if (guardedInterfaceSubtype(parent, i2, visited)) {
|
| + return true;
|
| + }
|
| }
|
| +
|
| + for (final parent in i1.mixins) {
|
| + if (guardedInterfaceSubtype(parent, i2, visited)) {
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + return false;
|
| }
|
|
|
| - /**
|
| - * Look for user defined tasks in comments and convert them into info level analysis issues.
|
| - *
|
| - * @param commentToken the comment token to analyze
|
| - */
|
| - void _scrapeTodoComment(sc.Token commentToken) {
|
| - JavaPatternMatcher matcher =
|
| - new JavaPatternMatcher(TodoCode.TODO_REGEX, commentToken.lexeme);
|
| - if (matcher.find()) {
|
| - int offset =
|
| - commentToken.offset + matcher.start() + matcher.group(1).length;
|
| - int length = matcher.group(2).length;
|
| - _errorReporter.reportErrorForOffset(
|
| - TodoCode.TODO, offset, length, [matcher.group(2)]);
|
| + bool _isSubtypeOf(DartType t1, DartType t2, Set<Element> visited,
|
| + {bool dynamicIsBottom: false}) {
|
| + // Guard recursive calls
|
| + _GuardedSubtypeChecker<DartType> guardedSubtype = _guard(_isSubtypeOf);
|
| +
|
| + if (t1 == t2) {
|
| + return true;
|
| + }
|
| +
|
| + // The types are void, dynamic, bottom, interface types, function types
|
| + // and type parameters. We proceed by eliminating these different classes
|
| + // from consideration.
|
| +
|
| + // Trivially true.
|
| + if (_isTop(t2, dynamicIsBottom: dynamicIsBottom) ||
|
| + _isBottom(t1, dynamicIsBottom: dynamicIsBottom)) {
|
| + return true;
|
| + }
|
| +
|
| + // Trivially false.
|
| + if (_isTop(t1, dynamicIsBottom: dynamicIsBottom) ||
|
| + _isBottom(t2, dynamicIsBottom: dynamicIsBottom)) {
|
| + return false;
|
| + }
|
| +
|
| + // S <: T where S is a type variable
|
| + // T is not dynamic or object (handled above)
|
| + // S != T (handled above)
|
| + // So only true if bound of S is S' and
|
| + // S' <: T
|
| + if (t1 is TypeParameterType) {
|
| + DartType bound = t1.element.bound;
|
| + if (bound == null) return false;
|
| + return guardedSubtype(bound, t2, visited);
|
| + }
|
| +
|
| + if (t2 is TypeParameterType) {
|
| + return false;
|
| + }
|
| +
|
| + if (t1.isVoid || t2.isVoid) {
|
| + return false;
|
| + }
|
| +
|
| + // We've eliminated void, dynamic, bottom, and type parameters. The only
|
| + // cases are the combinations of interface type and function type.
|
| +
|
| + // A function type can only subtype an interface type if
|
| + // the interface type is Function
|
| + if (t1 is FunctionType && t2 is InterfaceType) {
|
| + return t2.isDartCoreFunction;
|
| + }
|
| +
|
| + // An interface type can only subtype a function type if
|
| + // the interface type declares a call method with a type
|
| + // which is a super type of the function type.
|
| + if (t1 is InterfaceType && t2 is FunctionType) {
|
| + var callType = _getCallMethodType(t1);
|
| + return (callType != null) && _isFunctionSubtypeOf(callType, t2);
|
| + }
|
| +
|
| + // Two interface types
|
| + if (t1 is InterfaceType && t2 is InterfaceType) {
|
| + return _isInterfaceSubtypeOf(t1, t2, visited);
|
| }
|
| +
|
| + return _isFunctionSubtypeOf(t1 as FunctionType, t2 as FunctionType);
|
| + }
|
| +
|
| + // TODO(leafp): Document the rules in play here
|
| + bool _isTop(DartType t, {bool dynamicIsBottom: false}) {
|
| + return (t.isDynamic && !dynamicIsBottom) || t.isObject;
|
| }
|
| }
|
|
|
| /**
|
| - * Instances of the class `TypeOverrideManager` manage the ability to override the type of an
|
| - * element within a given context.
|
| + * Instances of this class manage the knowledge of what the set of subtypes are for a given type.
|
| */
|
| -class TypeOverrideManager {
|
| +class SubtypeManager {
|
| /**
|
| - * The current override scope, or `null` if no scope has been entered.
|
| + * A map between [ClassElement]s and a set of [ClassElement]s that are subtypes of the
|
| + * key.
|
| */
|
| - TypeOverrideManager_TypeOverrideScope currentScope;
|
| + HashMap<ClassElement, HashSet<ClassElement>> _subtypeMap =
|
| + new HashMap<ClassElement, HashSet<ClassElement>>();
|
|
|
| /**
|
| - * Apply a set of overrides that were previously captured.
|
| - *
|
| - * @param overrides the overrides to be applied
|
| + * The set of all [LibraryElement]s that have been visited by the manager. This is used both
|
| + * to prevent infinite loops in the recursive methods, and also as a marker for the scope of the
|
| + * libraries visited by this manager.
|
| */
|
| - void applyOverrides(Map<VariableElement, DartType> overrides) {
|
| - if (currentScope == null) {
|
| - throw new IllegalStateException("Cannot apply overrides without a scope");
|
| - }
|
| - currentScope.applyOverrides(overrides);
|
| - }
|
| + HashSet<LibraryElement> _visitedLibraries = new HashSet<LibraryElement>();
|
|
|
| /**
|
| - * Return a table mapping the elements whose type is overridden in the current scope to the
|
| - * overriding type.
|
| + * Given some [ClassElement], return the set of all subtypes, and subtypes of subtypes.
|
| *
|
| - * @return the overrides in the current scope
|
| + * @param classElement the class to recursively return the set of subtypes of
|
| */
|
| - Map<VariableElement, DartType> captureLocalOverrides() {
|
| - if (currentScope == null) {
|
| - throw new IllegalStateException(
|
| - "Cannot capture local overrides without a scope");
|
| - }
|
| - return currentScope.captureLocalOverrides();
|
| + HashSet<ClassElement> computeAllSubtypes(ClassElement classElement) {
|
| + // Ensure that we have generated the subtype map for the library
|
| + _computeSubtypesInLibrary(classElement.library);
|
| + // use the subtypeMap to compute the set of all subtypes and subtype's
|
| + // subtypes
|
| + HashSet<ClassElement> allSubtypes = new HashSet<ClassElement>();
|
| + _safelyComputeAllSubtypes(
|
| + classElement, new HashSet<ClassElement>(), allSubtypes);
|
| + return allSubtypes;
|
| }
|
|
|
| /**
|
| - * Return a map from the elements for the variables in the given list that have their types
|
| - * overridden to the overriding type.
|
| + * Given some [LibraryElement], visit all of the types in the library, the passed library,
|
| + * and any imported libraries, will be in the [visitedLibraries] set.
|
| *
|
| - * @param variableList the list of variables whose overriding types are to be captured
|
| - * @return a table mapping elements to their overriding types
|
| + * @param libraryElement the library to visit, it it hasn't been visited already
|
| + */
|
| + void ensureLibraryVisited(LibraryElement libraryElement) {
|
| + _computeSubtypesInLibrary(libraryElement);
|
| + }
|
| +
|
| + /**
|
| + * Given some [ClassElement], this method adds all of the pairs combinations of itself and
|
| + * all of its supertypes to the [subtypeMap] map.
|
| + *
|
| + * @param classElement the class element
|
| + */
|
| + void _computeSubtypesInClass(ClassElement classElement) {
|
| + InterfaceType supertypeType = classElement.supertype;
|
| + if (supertypeType != null) {
|
| + ClassElement supertypeElement = supertypeType.element;
|
| + if (supertypeElement != null) {
|
| + _putInSubtypeMap(supertypeElement, classElement);
|
| + }
|
| + }
|
| + List<InterfaceType> interfaceTypes = classElement.interfaces;
|
| + for (InterfaceType interfaceType in interfaceTypes) {
|
| + ClassElement interfaceElement = interfaceType.element;
|
| + if (interfaceElement != null) {
|
| + _putInSubtypeMap(interfaceElement, classElement);
|
| + }
|
| + }
|
| + List<InterfaceType> mixinTypes = classElement.mixins;
|
| + for (InterfaceType mixinType in mixinTypes) {
|
| + ClassElement mixinElement = mixinType.element;
|
| + if (mixinElement != null) {
|
| + _putInSubtypeMap(mixinElement, classElement);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Given some [CompilationUnitElement], this method calls
|
| + * [computeAllSubtypes] on all of the [ClassElement]s in the
|
| + * compilation unit.
|
| + *
|
| + * @param unitElement the compilation unit element
|
| + */
|
| + void _computeSubtypesInCompilationUnit(CompilationUnitElement unitElement) {
|
| + List<ClassElement> classElements = unitElement.types;
|
| + for (ClassElement classElement in classElements) {
|
| + _computeSubtypesInClass(classElement);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Given some [LibraryElement], this method calls
|
| + * [computeAllSubtypes] on all of the [ClassElement]s in the
|
| + * compilation unit, and itself for all imported and exported libraries. All visited libraries are
|
| + * added to the [visitedLibraries] set.
|
| + *
|
| + * @param libraryElement the library element
|
| + */
|
| + void _computeSubtypesInLibrary(LibraryElement libraryElement) {
|
| + if (libraryElement == null || _visitedLibraries.contains(libraryElement)) {
|
| + return;
|
| + }
|
| + _visitedLibraries.add(libraryElement);
|
| + _computeSubtypesInCompilationUnit(libraryElement.definingCompilationUnit);
|
| + List<CompilationUnitElement> parts = libraryElement.parts;
|
| + for (CompilationUnitElement part in parts) {
|
| + _computeSubtypesInCompilationUnit(part);
|
| + }
|
| + List<LibraryElement> imports = libraryElement.importedLibraries;
|
| + for (LibraryElement importElt in imports) {
|
| + _computeSubtypesInLibrary(importElt.library);
|
| + }
|
| + List<LibraryElement> exports = libraryElement.exportedLibraries;
|
| + for (LibraryElement exportElt in exports) {
|
| + _computeSubtypesInLibrary(exportElt.library);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Add some key/ value pair into the [subtypeMap] map.
|
| + *
|
| + * @param supertypeElement the key for the [subtypeMap] map
|
| + * @param subtypeElement the value for the [subtypeMap] map
|
| + */
|
| + void _putInSubtypeMap(
|
| + ClassElement supertypeElement, ClassElement subtypeElement) {
|
| + HashSet<ClassElement> subtypes = _subtypeMap[supertypeElement];
|
| + if (subtypes == null) {
|
| + subtypes = new HashSet<ClassElement>();
|
| + _subtypeMap[supertypeElement] = subtypes;
|
| + }
|
| + subtypes.add(subtypeElement);
|
| + }
|
| +
|
| + /**
|
| + * Given some [ClassElement] and a [HashSet<ClassElement>], this method recursively
|
| + * adds all of the subtypes of the [ClassElement] to the passed array.
|
| + *
|
| + * @param classElement the type to compute the set of subtypes of
|
| + * @param visitedClasses the set of class elements that this method has already recursively seen
|
| + * @param allSubtypes the computed set of subtypes of the passed class element
|
| + */
|
| + void _safelyComputeAllSubtypes(ClassElement classElement,
|
| + HashSet<ClassElement> visitedClasses, HashSet<ClassElement> allSubtypes) {
|
| + if (!visitedClasses.add(classElement)) {
|
| + // if this class has already been called on this class element
|
| + return;
|
| + }
|
| + HashSet<ClassElement> subtypes = _subtypeMap[classElement];
|
| + if (subtypes == null) {
|
| + return;
|
| + }
|
| + for (ClassElement subtype in subtypes) {
|
| + _safelyComputeAllSubtypes(subtype, visitedClasses, allSubtypes);
|
| + }
|
| + allSubtypes.addAll(subtypes);
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * Instances of the class `ToDoFinder` find to-do comments in Dart code.
|
| + */
|
| +class ToDoFinder {
|
| + /**
|
| + * The error reporter by which to-do comments will be reported.
|
| + */
|
| + final ErrorReporter _errorReporter;
|
| +
|
| + /**
|
| + * Initialize a newly created to-do finder to report to-do comments to the given reporter.
|
| + *
|
| + * @param errorReporter the error reporter by which to-do comments will be reported
|
| + */
|
| + ToDoFinder(this._errorReporter);
|
| +
|
| + /**
|
| + * Search the comments in the given compilation unit for to-do comments and report an error for
|
| + * each.
|
| + *
|
| + * @param unit the compilation unit containing the to-do comments
|
| + */
|
| + void findIn(CompilationUnit unit) {
|
| + _gatherTodoComments(unit.beginToken);
|
| + }
|
| +
|
| + /**
|
| + * Search the comment tokens reachable from the given token and create errors for each to-do
|
| + * comment.
|
| + *
|
| + * @param token the head of the list of tokens being searched
|
| + */
|
| + void _gatherTodoComments(sc.Token token) {
|
| + while (token != null && token.type != sc.TokenType.EOF) {
|
| + sc.Token commentToken = token.precedingComments;
|
| + while (commentToken != null) {
|
| + if (commentToken.type == sc.TokenType.SINGLE_LINE_COMMENT ||
|
| + commentToken.type == sc.TokenType.MULTI_LINE_COMMENT) {
|
| + _scrapeTodoComment(commentToken);
|
| + }
|
| + commentToken = commentToken.next;
|
| + }
|
| + token = token.next;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Look for user defined tasks in comments and convert them into info level analysis issues.
|
| + *
|
| + * @param commentToken the comment token to analyze
|
| + */
|
| + void _scrapeTodoComment(sc.Token commentToken) {
|
| + JavaPatternMatcher matcher =
|
| + new JavaPatternMatcher(TodoCode.TODO_REGEX, commentToken.lexeme);
|
| + if (matcher.find()) {
|
| + int offset =
|
| + commentToken.offset + matcher.start() + matcher.group(1).length;
|
| + int length = matcher.group(2).length;
|
| + _errorReporter.reportErrorForOffset(
|
| + TodoCode.TODO, offset, length, [matcher.group(2)]);
|
| + }
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * Instances of the class `TypeOverrideManager` manage the ability to override the type of an
|
| + * element within a given context.
|
| + */
|
| +class TypeOverrideManager {
|
| + /**
|
| + * The current override scope, or `null` if no scope has been entered.
|
| + */
|
| + TypeOverrideManager_TypeOverrideScope currentScope;
|
| +
|
| + /**
|
| + * Apply a set of overrides that were previously captured.
|
| + *
|
| + * @param overrides the overrides to be applied
|
| + */
|
| + void applyOverrides(Map<VariableElement, DartType> overrides) {
|
| + if (currentScope == null) {
|
| + throw new IllegalStateException("Cannot apply overrides without a scope");
|
| + }
|
| + currentScope.applyOverrides(overrides);
|
| + }
|
| +
|
| + /**
|
| + * Return a table mapping the elements whose type is overridden in the current scope to the
|
| + * overriding type.
|
| + *
|
| + * @return the overrides in the current scope
|
| + */
|
| + Map<VariableElement, DartType> captureLocalOverrides() {
|
| + if (currentScope == null) {
|
| + throw new IllegalStateException(
|
| + "Cannot capture local overrides without a scope");
|
| + }
|
| + return currentScope.captureLocalOverrides();
|
| + }
|
| +
|
| + /**
|
| + * Return a map from the elements for the variables in the given list that have their types
|
| + * overridden to the overriding type.
|
| + *
|
| + * @param variableList the list of variables whose overriding types are to be captured
|
| + * @return a table mapping elements to their overriding types
|
| */
|
| Map<VariableElement, DartType> captureOverrides(
|
| VariableDeclarationList variableList) {
|
| @@ -14906,481 +15238,174 @@ class TypeResolverVisitor extends ScopedVisitor {
|
| element.getAncestor((element) => element is FunctionTypeAliasElement);
|
| while (alias != null && alias.isSynthetic) {
|
| alias =
|
| - alias.getAncestor((element) => element is FunctionTypeAliasElement);
|
| - }
|
| - if (alias != null) {
|
| - aliasElement.typeParameters = alias.typeParameters;
|
| - type.typeArguments = alias.type.typeArguments;
|
| - } else {
|
| - type.typeArguments = DartType.EMPTY_LIST;
|
| - }
|
| - }
|
| - element.type = type;
|
| - }
|
| -
|
| - /**
|
| - * @return `true` if the name of the given [TypeName] is an built-in identifier.
|
| - */
|
| - static bool _isBuiltInIdentifier(TypeName node) {
|
| - sc.Token token = node.name.beginToken;
|
| - return token.type == sc.TokenType.KEYWORD;
|
| - }
|
| -
|
| - /**
|
| - * @return `true` if given [TypeName] is used as a type annotation.
|
| - */
|
| - static bool _isTypeAnnotation(TypeName node) {
|
| - AstNode parent = node.parent;
|
| - if (parent is VariableDeclarationList) {
|
| - return identical(parent.type, node);
|
| - }
|
| - if (parent is FieldFormalParameter) {
|
| - return identical(parent.type, node);
|
| - }
|
| - if (parent is SimpleFormalParameter) {
|
| - return identical(parent.type, node);
|
| - }
|
| - return false;
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * The interface `TypeSystem` defines the behavior of an object representing
|
| - * the type system. This provides a common location to put methods that act on
|
| - * types but may need access to more global data structures, and it paves the
|
| - * way for a possible future where we may wish to make the type system
|
| - * pluggable.
|
| - */
|
| -abstract class TypeSystem {
|
| - /**
|
| - * Create either a strong mode or regular type system based on context.
|
| - */
|
| - static TypeSystem create(AnalysisContext context) {
|
| - return (context.analysisOptions.strongMode)
|
| - ? new StrongTypeSystemImpl()
|
| - : new TypeSystemImpl();
|
| - }
|
| -
|
| - /**
|
| - * Compute the least upper bound of two types.
|
| - */
|
| - DartType getLeastUpperBound(
|
| - TypeProvider typeProvider, DartType type1, DartType type2);
|
| -
|
| - /**
|
| - * Return `true` if the [leftType] is assignable to the [rightType] (that is,
|
| - * if leftType <==> rightType).
|
| - */
|
| - bool isAssignableTo(DartType leftType, DartType rightType);
|
| -
|
| - /**
|
| - * Return `true` if the [leftType] is a subtype of the [rightType] (that is,
|
| - * if leftType <: rightType).
|
| - */
|
| - bool isSubtypeOf(DartType leftType, DartType rightType);
|
| -}
|
| -
|
| -/**
|
| - * Implementation of [TypeSystem] using the rules in the Dart specification.
|
| - */
|
| -class TypeSystemImpl implements TypeSystem {
|
| - TypeSystemImpl();
|
| -
|
| - @override
|
| - DartType getLeastUpperBound(
|
| - TypeProvider typeProvider, DartType type1, DartType type2) {
|
| - // The least upper bound relation is reflexive.
|
| - if (identical(type1, type2)) {
|
| - return type1;
|
| - }
|
| - // The least upper bound of dynamic and any type T is dynamic.
|
| - if (type1.isDynamic) {
|
| - return type1;
|
| - }
|
| - if (type2.isDynamic) {
|
| - return type2;
|
| - }
|
| - // The least upper bound of void and any type T != dynamic is void.
|
| - if (type1.isVoid) {
|
| - return type1;
|
| - }
|
| - if (type2.isVoid) {
|
| - return type2;
|
| - }
|
| - // The least upper bound of bottom and any type T is T.
|
| - if (type1.isBottom) {
|
| - return type2;
|
| - }
|
| - if (type2.isBottom) {
|
| - return type1;
|
| - }
|
| - // Let U be a type variable with upper bound B. The least upper bound of U
|
| - // and a type T is the least upper bound of B and T.
|
| - while (type1 is TypeParameterType) {
|
| - // TODO(paulberry): is this correct in the complex of F-bounded
|
| - // polymorphism?
|
| - DartType bound = (type1 as TypeParameterType).element.bound;
|
| - if (bound == null) {
|
| - bound = typeProvider.objectType;
|
| - }
|
| - type1 = bound;
|
| - }
|
| - while (type2 is TypeParameterType) {
|
| - // TODO(paulberry): is this correct in the context of F-bounded
|
| - // polymorphism?
|
| - DartType bound = (type2 as TypeParameterType).element.bound;
|
| - if (bound == null) {
|
| - bound = typeProvider.objectType;
|
| - }
|
| - type2 = bound;
|
| - }
|
| - // The least upper bound of a function type and an interface type T is the
|
| - // least upper bound of Function and T.
|
| - if (type1 is FunctionType && type2 is InterfaceType) {
|
| - type1 = typeProvider.functionType;
|
| - }
|
| - if (type2 is FunctionType && type1 is InterfaceType) {
|
| - type2 = typeProvider.functionType;
|
| - }
|
| -
|
| - // At this point type1 and type2 should both either be interface types or
|
| - // function types.
|
| - if (type1 is InterfaceType && type2 is InterfaceType) {
|
| - InterfaceType result =
|
| - InterfaceTypeImpl.computeLeastUpperBound(type1, type2);
|
| - if (result == null) {
|
| - return typeProvider.dynamicType;
|
| - }
|
| - return result;
|
| - } else if (type1 is FunctionType && type2 is FunctionType) {
|
| - FunctionType result =
|
| - FunctionTypeImpl.computeLeastUpperBound(type1, type2);
|
| - if (result == null) {
|
| - return typeProvider.functionType;
|
| - }
|
| - return result;
|
| - } else {
|
| - // Should never happen. As a defensive measure, return the dynamic type.
|
| - assert(false);
|
| - return typeProvider.dynamicType;
|
| - }
|
| - }
|
| -
|
| - @override
|
| - bool isAssignableTo(DartType leftType, DartType rightType) {
|
| - return leftType.isAssignableTo(rightType);
|
| - }
|
| -
|
| - @override
|
| - bool isSubtypeOf(DartType leftType, DartType rightType) {
|
| - return leftType.isSubtypeOf(rightType);
|
| - }
|
| -}
|
| -
|
| -typedef bool _GuardedSubtypeChecker<T>(T t1, T t2, Set<Element> visited);
|
| -typedef bool _SubtypeChecker<T>(T t1, T t2);
|
| -
|
| -/**
|
| - * Implementation of [TypeSystem] using the strong mode rules.
|
| - * https://github.com/dart-lang/dev_compiler/blob/master/STRONG_MODE.md
|
| - */
|
| -class StrongTypeSystemImpl implements TypeSystem {
|
| - StrongTypeSystemImpl();
|
| -
|
| - final _specTypeSystem = new TypeSystemImpl();
|
| -
|
| - @override
|
| - DartType getLeastUpperBound(
|
| - TypeProvider typeProvider, DartType type1, DartType type2) {
|
| - // TODO(leafp): Implement a strong mode version of this.
|
| - return _specTypeSystem.getLeastUpperBound(typeProvider, type1, type2);
|
| - }
|
| -
|
| - // TODO(leafp): Document the rules in play here
|
| - @override
|
| - bool isAssignableTo(DartType fromType, DartType toType) {
|
| - // An actual subtype
|
| - if (isSubtypeOf(fromType, toType)) {
|
| - return true;
|
| - }
|
| -
|
| - // Don't allow implicit downcasts between function types
|
| - // and call method objects, as these will almost always fail.
|
| - if ((fromType is FunctionType && _getCallMethodType(toType) != null) ||
|
| - (toType is FunctionType && _getCallMethodType(fromType) != null)) {
|
| - return false;
|
| - }
|
| -
|
| - // If the subtype relation goes the other way, allow the implicit downcast.
|
| - // TODO(leafp): Emit warnings and hints for these in some way.
|
| - // TODO(leafp): Consider adding a flag to disable these? Or just rely on
|
| - // --warnings-as-errors?
|
| - if (isSubtypeOf(toType, fromType) ||
|
| - _specTypeSystem.isAssignableTo(toType, fromType)) {
|
| - // TODO(leafp): error if type is known to be exact (literal,
|
| - // instance creation).
|
| - // TODO(leafp): Warn on composite downcast.
|
| - // TODO(leafp): hint on object/dynamic downcast.
|
| - // TODO(leafp): Consider allowing assignment casts.
|
| - return true;
|
| - }
|
| -
|
| - return false;
|
| - }
|
| -
|
| - bool _isBottom(DartType t, {bool dynamicIsBottom: false}) {
|
| - return (t.isDynamic && dynamicIsBottom) || t.isBottom;
|
| - }
|
| -
|
| - bool _isTop(DartType t, {bool dynamicIsBottom: false}) {
|
| - return (t.isDynamic && !dynamicIsBottom) || t.isObject;
|
| - }
|
| -
|
| - // Given a type t, if t is an interface type with a call method
|
| - // defined, return the function type for the call method, otherwise
|
| - // return null.
|
| - FunctionType _getCallMethodType(DartType t) {
|
| - if (t is InterfaceType) {
|
| - ClassElement element = t.element;
|
| - InheritanceManager manager = new InheritanceManager(element.library);
|
| - FunctionType callType = manager.lookupMemberType(t, "call");
|
| - return callType;
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - /**
|
| - * Check that [f1] is a subtype of [f2].
|
| - * [fuzzyArrows] indicates whether or not the f1 and f2 should be
|
| - * treated as fuzzy arrow types (and hence dynamic parameters to f2 treated
|
| - * as bottom).
|
| - */
|
| - bool _isFunctionSubtypeOf(FunctionType f1, FunctionType f2,
|
| - {bool fuzzyArrows: true}) {
|
| - final r1s = f1.normalParameterTypes;
|
| - final o1s = f1.optionalParameterTypes;
|
| - final n1s = f1.namedParameterTypes;
|
| - final r2s = f2.normalParameterTypes;
|
| - final o2s = f2.optionalParameterTypes;
|
| - final n2s = f2.namedParameterTypes;
|
| - final ret1 = f1.returnType;
|
| - final ret2 = f2.returnType;
|
| -
|
| - // A -> B <: C -> D if C <: A and
|
| - // either D is void or B <: D
|
| - if (!ret2.isVoid && !isSubtypeOf(ret1, ret2)) {
|
| - return false;
|
| - }
|
| -
|
| - // Reject if one has named and the other has optional
|
| - if (n1s.length > 0 && o2s.length > 0) {
|
| - return false;
|
| - }
|
| - if (n2s.length > 0 && o1s.length > 0) {
|
| - return false;
|
| - }
|
| -
|
| - // Rebind _isSubtypeOf for convenience
|
| - _SubtypeChecker<DartType> parameterSubtype = (DartType t1, DartType t2) =>
|
| - _isSubtypeOf(t1, t2, null, dynamicIsBottom: fuzzyArrows);
|
| -
|
| - // f2 has named parameters
|
| - if (n2s.length > 0) {
|
| - // Check that every named parameter in f2 has a match in f1
|
| - for (String k2 in n2s.keys) {
|
| - if (!n1s.containsKey(k2)) {
|
| - return false;
|
| - }
|
| - if (!parameterSubtype(n2s[k2], n1s[k2])) {
|
| - return false;
|
| - }
|
| - }
|
| - }
|
| - // If we get here, we either have no named parameters,
|
| - // or else the named parameters match and we have no optional
|
| - // parameters
|
| -
|
| - // If f1 has more required parameters, reject
|
| - if (r1s.length > r2s.length) {
|
| - return false;
|
| - }
|
| -
|
| - // If f2 has more required + optional parameters, reject
|
| - if (r2s.length + o2s.length > r1s.length + o1s.length) {
|
| - return false;
|
| - }
|
| -
|
| - // The parameter lists must look like the following at this point
|
| - // where rrr is a region of required, and ooo is a region of optionals.
|
| - // f1: rrr ooo ooo ooo
|
| - // f2: rrr rrr ooo
|
| - int rr = r1s.length; // required in both
|
| - int or = r2s.length - r1s.length; // optional in f1, required in f2
|
| - int oo = o2s.length; // optional in both
|
| -
|
| - for (int i = 0; i < rr; ++i) {
|
| - if (!parameterSubtype(r2s[i], r1s[i])) {
|
| - return false;
|
| - }
|
| - }
|
| - for (int i = 0, j = rr; i < or; ++i, ++j) {
|
| - if (!parameterSubtype(r2s[j], o1s[i])) {
|
| - return false;
|
| - }
|
| - }
|
| - for (int i = or, j = 0; i < oo; ++i, ++j) {
|
| - if (!parameterSubtype(o2s[j], o1s[i])) {
|
| - return false;
|
| + alias.getAncestor((element) => element is FunctionTypeAliasElement);
|
| + }
|
| + if (alias != null) {
|
| + aliasElement.typeParameters = alias.typeParameters;
|
| + type.typeArguments = alias.type.typeArguments;
|
| + } else {
|
| + type.typeArguments = DartType.EMPTY_LIST;
|
| }
|
| }
|
| - return true;
|
| + element.type = type;
|
| }
|
|
|
| - // Guard against loops in the class hierarchy
|
| - _GuardedSubtypeChecker<DartType> _guard(
|
| - _GuardedSubtypeChecker<DartType> check) {
|
| - return (DartType t1, DartType t2, Set<Element> visited) {
|
| - Element element = t1.element;
|
| - if (visited == null) {
|
| - visited = new HashSet<Element>();
|
| - }
|
| - if (element == null || !visited.add(element)) {
|
| - return false;
|
| - }
|
| - try {
|
| - return check(t1, t2, visited);
|
| - } finally {
|
| - visited.remove(element);
|
| - }
|
| - };
|
| + /**
|
| + * @return `true` if the name of the given [TypeName] is an built-in identifier.
|
| + */
|
| + static bool _isBuiltInIdentifier(TypeName node) {
|
| + sc.Token token = node.name.beginToken;
|
| + return token.type == sc.TokenType.KEYWORD;
|
| }
|
|
|
| - bool _isInterfaceSubtypeOf(
|
| - InterfaceType i1, InterfaceType i2, Set<Element> visited) {
|
| - // Guard recursive calls
|
| - _GuardedSubtypeChecker<InterfaceType> guardedInterfaceSubtype =
|
| - _guard(_isInterfaceSubtypeOf);
|
| -
|
| - if (i1 == i2) {
|
| - return true;
|
| - }
|
| -
|
| - if (i1.element == i2.element) {
|
| - List<DartType> tArgs1 = i1.typeArguments;
|
| - List<DartType> tArgs2 = i2.typeArguments;
|
| -
|
| - assert(tArgs1.length == tArgs2.length);
|
| -
|
| - for (int i = 0; i < tArgs1.length; i++) {
|
| - DartType t1 = tArgs1[i];
|
| - DartType t2 = tArgs2[i];
|
| - if (!isSubtypeOf(t1, t2)) {
|
| - return false;
|
| - }
|
| - }
|
| - return true;
|
| + /**
|
| + * @return `true` if given [TypeName] is used as a type annotation.
|
| + */
|
| + static bool _isTypeAnnotation(TypeName node) {
|
| + AstNode parent = node.parent;
|
| + if (parent is VariableDeclarationList) {
|
| + return identical(parent.type, node);
|
| }
|
| -
|
| - if (i2.isDartCoreFunction && i1.element.getMethod("call") != null) {
|
| - return true;
|
| + if (parent is FieldFormalParameter) {
|
| + return identical(parent.type, node);
|
| }
|
| -
|
| - if (i1.isObject) {
|
| - return false;
|
| + if (parent is SimpleFormalParameter) {
|
| + return identical(parent.type, node);
|
| }
|
| + return false;
|
| + }
|
| +}
|
|
|
| - if (guardedInterfaceSubtype(i1.superclass, i2, visited)) {
|
| - return true;
|
| - }
|
| +/**
|
| + * The interface `TypeSystem` defines the behavior of an object representing
|
| + * the type system. This provides a common location to put methods that act on
|
| + * types but may need access to more global data structures, and it paves the
|
| + * way for a possible future where we may wish to make the type system
|
| + * pluggable.
|
| + */
|
| +abstract class TypeSystem {
|
| + /**
|
| + * Compute the least upper bound of two types.
|
| + */
|
| + DartType getLeastUpperBound(
|
| + TypeProvider typeProvider, DartType type1, DartType type2);
|
|
|
| - for (final parent in i1.interfaces) {
|
| - if (guardedInterfaceSubtype(parent, i2, visited)) {
|
| - return true;
|
| - }
|
| - }
|
| + /**
|
| + * Return `true` if the [leftType] is assignable to the [rightType] (that is,
|
| + * if leftType <==> rightType).
|
| + */
|
| + bool isAssignableTo(DartType leftType, DartType rightType);
|
|
|
| - for (final parent in i1.mixins) {
|
| - if (guardedInterfaceSubtype(parent, i2, visited)) {
|
| - return true;
|
| - }
|
| - }
|
| + /**
|
| + * Return `true` if the [leftType] is a subtype of the [rightType] (that is,
|
| + * if leftType <: rightType).
|
| + */
|
| + bool isSubtypeOf(DartType leftType, DartType rightType);
|
|
|
| - return false;
|
| + /**
|
| + * Create either a strong mode or regular type system based on context.
|
| + */
|
| + static TypeSystem create(AnalysisContext context) {
|
| + return (context.analysisOptions.strongMode)
|
| + ? new StrongTypeSystemImpl()
|
| + : new TypeSystemImpl();
|
| }
|
| +}
|
|
|
| - bool _isSubtypeOf(DartType t1, DartType t2, Set<Element> visited,
|
| - {bool dynamicIsBottom: false}) {
|
| - // Guard recursive calls
|
| - _GuardedSubtypeChecker<DartType> guardedSubtype = _guard(_isSubtypeOf);
|
| +/**
|
| + * Implementation of [TypeSystem] using the rules in the Dart specification.
|
| + */
|
| +class TypeSystemImpl implements TypeSystem {
|
| + TypeSystemImpl();
|
|
|
| - if (t1 == t2) {
|
| - return true;
|
| + @override
|
| + DartType getLeastUpperBound(
|
| + TypeProvider typeProvider, DartType type1, DartType type2) {
|
| + // The least upper bound relation is reflexive.
|
| + if (identical(type1, type2)) {
|
| + return type1;
|
| }
|
| -
|
| - // The types are void, dynamic, bottom, interface types, function types
|
| - // and type parameters. We proceed by eliminating these different classes
|
| - // from consideration.
|
| -
|
| - // Trivially true.
|
| - if (_isTop(t2, dynamicIsBottom: dynamicIsBottom) ||
|
| - _isBottom(t1, dynamicIsBottom: dynamicIsBottom)) {
|
| - return true;
|
| + // The least upper bound of dynamic and any type T is dynamic.
|
| + if (type1.isDynamic) {
|
| + return type1;
|
| }
|
| -
|
| - // Trivially false.
|
| - if (_isTop(t1, dynamicIsBottom: dynamicIsBottom) ||
|
| - _isBottom(t2, dynamicIsBottom: dynamicIsBottom)) {
|
| - return false;
|
| + if (type2.isDynamic) {
|
| + return type2;
|
| }
|
| -
|
| - // S <: T where S is a type variable
|
| - // T is not dynamic or object (handled above)
|
| - // S != T (handled above)
|
| - // So only true if bound of S is S' and
|
| - // S' <: T
|
| - if (t1 is TypeParameterType) {
|
| - DartType bound = t1.element.bound;
|
| - if (bound == null) return false;
|
| - return guardedSubtype(bound, t2, visited);
|
| + // The least upper bound of void and any type T != dynamic is void.
|
| + if (type1.isVoid) {
|
| + return type1;
|
| }
|
| -
|
| - if (t2 is TypeParameterType) {
|
| - return false;
|
| + if (type2.isVoid) {
|
| + return type2;
|
| }
|
| -
|
| - if (t1.isVoid || t2.isVoid) {
|
| - return false;
|
| + // The least upper bound of bottom and any type T is T.
|
| + if (type1.isBottom) {
|
| + return type2;
|
| }
|
| -
|
| - // We've eliminated void, dynamic, bottom, and type parameters. The only
|
| - // cases are the combinations of interface type and function type.
|
| -
|
| - // A function type can only subtype an interface type if
|
| - // the interface type is Function
|
| - if (t1 is FunctionType && t2 is InterfaceType) {
|
| - return t2.isDartCoreFunction;
|
| + if (type2.isBottom) {
|
| + return type1;
|
| }
|
| -
|
| - // An interface type can only subtype a function type if
|
| - // the interface type declares a call method with a type
|
| - // which is a super type of the function type.
|
| - if (t1 is InterfaceType && t2 is FunctionType) {
|
| - var callType = _getCallMethodType(t1);
|
| - return (callType != null) && _isFunctionSubtypeOf(callType, t2);
|
| + // Let U be a type variable with upper bound B. The least upper bound of U
|
| + // and a type T is the least upper bound of B and T.
|
| + while (type1 is TypeParameterType) {
|
| + // TODO(paulberry): is this correct in the complex of F-bounded
|
| + // polymorphism?
|
| + DartType bound = (type1 as TypeParameterType).element.bound;
|
| + if (bound == null) {
|
| + bound = typeProvider.objectType;
|
| + }
|
| + type1 = bound;
|
| + }
|
| + while (type2 is TypeParameterType) {
|
| + // TODO(paulberry): is this correct in the context of F-bounded
|
| + // polymorphism?
|
| + DartType bound = (type2 as TypeParameterType).element.bound;
|
| + if (bound == null) {
|
| + bound = typeProvider.objectType;
|
| + }
|
| + type2 = bound;
|
| + }
|
| + // The least upper bound of a function type and an interface type T is the
|
| + // least upper bound of Function and T.
|
| + if (type1 is FunctionType && type2 is InterfaceType) {
|
| + type1 = typeProvider.functionType;
|
| + }
|
| + if (type2 is FunctionType && type1 is InterfaceType) {
|
| + type2 = typeProvider.functionType;
|
| }
|
|
|
| - // Two interface types
|
| - if (t1 is InterfaceType && t2 is InterfaceType) {
|
| - return _isInterfaceSubtypeOf(t1, t2, visited);
|
| + // At this point type1 and type2 should both either be interface types or
|
| + // function types.
|
| + if (type1 is InterfaceType && type2 is InterfaceType) {
|
| + InterfaceType result =
|
| + InterfaceTypeImpl.computeLeastUpperBound(type1, type2);
|
| + if (result == null) {
|
| + return typeProvider.dynamicType;
|
| + }
|
| + return result;
|
| + } else if (type1 is FunctionType && type2 is FunctionType) {
|
| + FunctionType result =
|
| + FunctionTypeImpl.computeLeastUpperBound(type1, type2);
|
| + if (result == null) {
|
| + return typeProvider.functionType;
|
| + }
|
| + return result;
|
| + } else {
|
| + // Should never happen. As a defensive measure, return the dynamic type.
|
| + assert(false);
|
| + return typeProvider.dynamicType;
|
| }
|
| + }
|
|
|
| - return _isFunctionSubtypeOf(t1 as FunctionType, t2 as FunctionType);
|
| + @override
|
| + bool isAssignableTo(DartType leftType, DartType rightType) {
|
| + return leftType.isAssignableTo(rightType);
|
| }
|
|
|
| - // TODO(leafp): Document the rules in play here
|
| @override
|
| bool isSubtypeOf(DartType leftType, DartType rightType) {
|
| - return _isSubtypeOf(leftType, rightType, null);
|
| + return leftType.isSubtypeOf(rightType);
|
| }
|
| }
|
|
|
|
|