Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(142)

Unified Diff: pkg/analyzer/lib/src/generated/resolver.dart

Issue 1376473007: Add the 'docRange' property and use it to compute documentation comment. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/analyzer/lib/src/generated/engine.dart ('k') | pkg/analyzer/lib/src/generated/testing/ast_factory.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
}
}
« no previous file with comments | « pkg/analyzer/lib/src/generated/engine.dart ('k') | pkg/analyzer/lib/src/generated/testing/ast_factory.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698