| Index: packages/analyzer/lib/src/generated/static_type_analyzer.dart
|
| diff --git a/analyzer/lib/src/generated/static_type_analyzer.dart b/packages/analyzer/lib/src/generated/static_type_analyzer.dart
|
| similarity index 86%
|
| rename from analyzer/lib/src/generated/static_type_analyzer.dart
|
| rename to packages/analyzer/lib/src/generated/static_type_analyzer.dart
|
| index 096c14b0abb4c95b4f5ca10dea94c34e46698db6..7362c4de016b5baacdaf6d352a1cd2f69afa72a6 100644
|
| --- a/analyzer/lib/src/generated/static_type_analyzer.dart
|
| +++ b/packages/analyzer/lib/src/generated/static_type_analyzer.dart
|
| @@ -58,6 +58,11 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
|
| InterfaceType thisType;
|
|
|
| /**
|
| + * Are we running in strong mode or not.
|
| + */
|
| + bool _strongMode;
|
| +
|
| + /**
|
| * The object keeping track of which elements have had their types overridden.
|
| */
|
| TypeOverrideManager _overrideManager;
|
| @@ -80,10 +85,11 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
|
| */
|
| StaticTypeAnalyzer(this._resolver) {
|
| _typeProvider = _resolver.typeProvider;
|
| + _typeSystem = _resolver.typeSystem;
|
| _dynamicType = _typeProvider.dynamicType;
|
| _overrideManager = _resolver.overrideManager;
|
| _promoteManager = _resolver.promoteManager;
|
| - _typeSystem = new TypeSystemImpl(_typeProvider);
|
| + _strongMode = _resolver.definingLibrary.context.analysisOptions.strongMode;
|
| }
|
|
|
| /**
|
| @@ -303,6 +309,15 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
|
| return null;
|
| }
|
|
|
| + @override
|
| + Object visitDeclaredIdentifier(DeclaredIdentifier node) {
|
| + super.visitDeclaredIdentifier(node);
|
| + if (_strongMode) {
|
| + _inferForEachLoopVariableType(node);
|
| + }
|
| + return null;
|
| + }
|
| +
|
| /**
|
| * The Dart Language Specification, 12.3: <blockquote>The static type of a literal double is
|
| * double.</blockquote>
|
| @@ -320,7 +335,9 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
|
| node.element as ExecutableElementImpl;
|
| functionElement.returnType =
|
| _computeStaticReturnTypeOfFunctionDeclaration(node);
|
| - _recordPropagatedTypeOfFunction(functionElement, function.body);
|
| + if (node.parent is FunctionDeclarationStatement) {
|
| + _recordPropagatedTypeOfFunction(functionElement, function.body);
|
| + }
|
| _recordStaticType(function, functionElement.type);
|
| return null;
|
| }
|
| @@ -543,8 +560,10 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
|
| }
|
| }
|
| }
|
| - _recordStaticType(node, _typeProvider.mapType
|
| - .substitute4(<DartType>[staticKeyType, staticValueType]));
|
| + _recordStaticType(
|
| + node,
|
| + _typeProvider.mapType
|
| + .substitute4(<DartType>[staticKeyType, staticValueType]));
|
| return null;
|
| }
|
|
|
| @@ -606,6 +625,15 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
|
| // Check for special cases.
|
| bool needPropagatedType = true;
|
| String methodName = methodNameNode.name;
|
| + if (_strongMode) {
|
| + // TODO(leafp): Revisit this. It's here to associate a type with the
|
| + // method name, which is important to the DDC backend (but apparently
|
| + // no-one else). Not sure that there's a problem having this here, but
|
| + // it's a little ad hoc.
|
| + visitSimpleIdentifier(methodNameNode);
|
| +
|
| + _inferMethodInvocation(node);
|
| + }
|
| if (methodName == "then") {
|
| Expression target = node.realTarget;
|
| if (target != null) {
|
| @@ -870,8 +898,11 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
|
| } else if (staticElement is VariableElement) {
|
| staticType = staticElement.type;
|
| }
|
| - _recordStaticType(prefixedIdentifier, staticType);
|
| - _recordStaticType(node, staticType);
|
| + if (!(_strongMode &&
|
| + _inferObjectAccess(node, staticType, prefixedIdentifier))) {
|
| + _recordStaticType(prefixedIdentifier, staticType);
|
| + _recordStaticType(node, staticType);
|
| + }
|
| Element propagatedElement = prefixedIdentifier.propagatedElement;
|
| // HACK: special case for object getters ([hashCode] and [runtimeType]) on
|
| // dynamic expressions. More special cases in [visitMethodInvocation].
|
| @@ -999,8 +1030,10 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
|
| } else {
|
| // TODO(brianwilkerson) Report this internal error.
|
| }
|
| - _recordStaticType(propertyName, staticType);
|
| - _recordStaticType(node, staticType);
|
| + if (!(_strongMode && _inferObjectAccess(node, staticType, propertyName))) {
|
| + _recordStaticType(propertyName, staticType);
|
| + _recordStaticType(node, staticType);
|
| + }
|
| Element propagatedElement = propertyName.propagatedElement;
|
| DartType propagatedType = _overrideManager.getType(propagatedElement);
|
| if (propagatedElement is MethodElement) {
|
| @@ -1182,6 +1215,9 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
|
| @override
|
| Object visitVariableDeclaration(VariableDeclaration node) {
|
| Expression initializer = node.initializer;
|
| + if (_strongMode) {
|
| + _inferLocalVariableType(node, initializer);
|
| + }
|
| if (initializer != null) {
|
| DartType rightType = initializer.bestType;
|
| SimpleIdentifier name = node.name;
|
| @@ -1211,7 +1247,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
|
| staticType2 = _dynamicType;
|
| }
|
| DartType staticType =
|
| - _typeSystem.getLeastUpperBound(staticType1, staticType2);
|
| + _typeSystem.getLeastUpperBound(_typeProvider, staticType1, staticType2);
|
| if (staticType == null) {
|
| staticType = _dynamicType;
|
| }
|
| @@ -1225,8 +1261,8 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
|
| if (propagatedType2 == null) {
|
| propagatedType2 = staticType2;
|
| }
|
| - DartType propagatedType =
|
| - _typeSystem.getLeastUpperBound(propagatedType1, propagatedType2);
|
| + DartType propagatedType = _typeSystem.getLeastUpperBound(
|
| + _typeProvider, propagatedType1, propagatedType2);
|
| _resolver.recordPropagatedTypeIfBetter(node, propagatedType);
|
| }
|
| }
|
| @@ -1276,7 +1312,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
|
| if (body is BlockFunctionBody) {
|
| _StaticTypeAnalyzer_computePropagatedReturnTypeOfFunction visitor =
|
| new _StaticTypeAnalyzer_computePropagatedReturnTypeOfFunction(
|
| - _typeSystem);
|
| + _typeProvider, _typeSystem);
|
| body.accept(visitor);
|
| return visitor.result;
|
| }
|
| @@ -1382,6 +1418,43 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
|
| }
|
| }
|
|
|
| + // TODO(vsm): Use leafp's matchType here?
|
| + DartType _findIteratedType(InterfaceType type, DartType targetType) {
|
| + // Set by _find if match is found
|
| + DartType result = null;
|
| + // Elements we've already visited on a given inheritance path.
|
| + HashSet<ClassElement> visitedClasses = null;
|
| +
|
| + bool _find(InterfaceType type) {
|
| + ClassElement element = type.element;
|
| + if (type == _typeProvider.objectType || element == null) {
|
| + return false;
|
| + }
|
| + if (element == targetType.element) {
|
| + List<DartType> typeArguments = type.typeArguments;
|
| + assert(typeArguments.length == 1);
|
| + result = typeArguments[0];
|
| + return true;
|
| + }
|
| + if (visitedClasses == null) {
|
| + visitedClasses = new HashSet<ClassElement>();
|
| + }
|
| + // Already visited this class along this path
|
| + if (!visitedClasses.add(element)) {
|
| + return false;
|
| + }
|
| + try {
|
| + return _find(type.superclass) ||
|
| + type.interfaces.any(_find) ||
|
| + type.mixins.any(_find);
|
| + } finally {
|
| + visitedClasses.remove(element);
|
| + }
|
| + }
|
| + _find(type);
|
| + return result;
|
| + }
|
| +
|
| /**
|
| * Return the best type of the given [expression].
|
| */
|
| @@ -1604,6 +1677,174 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
|
| }
|
|
|
| /**
|
| + * Given a declared identifier from a foreach loop, attempt to infer
|
| + * a type for it if one is not already present. Inference is based
|
| + * on the type of the iterator or stream over which the foreach loop
|
| + * is defined.
|
| + */
|
| + void _inferForEachLoopVariableType(DeclaredIdentifier loopVariable) {
|
| + if (loopVariable != null &&
|
| + loopVariable.type == null &&
|
| + loopVariable.parent is ForEachStatement) {
|
| + ForEachStatement loop = loopVariable.parent;
|
| + if (loop.iterable != null) {
|
| + Expression expr = loop.iterable;
|
| + LocalVariableElementImpl element = loopVariable.element;
|
| + DartType exprType = expr.staticType;
|
| + if (exprType is InterfaceType) {
|
| + DartType targetType = (loop.awaitKeyword == null)
|
| + ? _typeProvider.iterableType
|
| + : _typeProvider.streamType;
|
| + DartType iteratedType = _findIteratedType(exprType, targetType);
|
| + if (element != null && iteratedType != null) {
|
| + element.type = iteratedType;
|
| + loopVariable.identifier.staticType = iteratedType;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Given a method invocation [node], attempt to infer a better
|
| + * type for the result.
|
| + */
|
| + bool _inferMethodInvocation(MethodInvocation node) {
|
| + return _inferMethodInvocationObject(node) ||
|
| + _inferMethodInvocationGeneric(node) ||
|
| + _inferMethodInvocationInlineJS(node);
|
| + }
|
| +
|
| + /**
|
| + * Given a method invocation [node], attempt to infer a better
|
| + * type for the result using an ad-hoc list of psuedo-generic methods.
|
| + */
|
| + bool _inferMethodInvocationGeneric(MethodInvocation node) {
|
| + DartType inferredType = _matchGeneric(node);
|
| + // TODO(vsm): If the inferred type is not a subtype,
|
| + // should we use a GLB instead?
|
| + if (inferredType != null &&
|
| + _typeSystem.isSubtypeOf(inferredType, node.staticType)) {
|
| + _recordStaticType(node, inferredType);
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| + * Given a method invocation [node], attempt to infer a better
|
| + * type for the result if it is an inline JS invocation
|
| + */
|
| + bool _inferMethodInvocationInlineJS(MethodInvocation node) {
|
| + Element e = node.methodName.staticElement;
|
| + if (e is FunctionElement &&
|
| + e.library.source.uri.toString() == 'dart:_foreign_helper' &&
|
| + e.name == 'JS') {
|
| + String typeStr = _getFirstArgumentAsString(node.argumentList);
|
| + DartType returnType = null;
|
| + if (typeStr == '-dynamic') {
|
| + returnType = _typeProvider.bottomType;
|
| + } else {
|
| + returnType = _getElementNameAsType(
|
| + _typeProvider.objectType.element.library, typeStr, null);
|
| + }
|
| + if (returnType != null) {
|
| + _recordStaticType(node, returnType);
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| + * Given a method invocation [node], attempt to infer a better
|
| + * type for the result if the target is dynamic and the method
|
| + * being called is one of the object methods.
|
| + */
|
| + bool _inferMethodInvocationObject(MethodInvocation node) {
|
| + // If we have a call like `toString()` or `libraryPrefix.toString()` don't
|
| + // infer it.
|
| + Expression target = node.realTarget;
|
| + if (target == null ||
|
| + target is SimpleIdentifier && target.staticElement is PrefixElement) {
|
| + return false;
|
| + }
|
| +
|
| + // Object methods called on dynamic targets can have their types improved.
|
| + String name = node.methodName.name;
|
| + MethodElement inferredElement =
|
| + _typeProvider.objectType.element.getMethod(name);
|
| + if (inferredElement == null || inferredElement.isStatic) {
|
| + return false;
|
| + }
|
| + DartType inferredType = inferredElement.type;
|
| + DartType nodeType = node.staticType;
|
| + if (nodeType != null &&
|
| + nodeType.isDynamic &&
|
| + inferredType is FunctionType &&
|
| + inferredType.parameters.isEmpty &&
|
| + node.argumentList.arguments.isEmpty &&
|
| + _typeProvider.nonSubtypableTypes.contains(inferredType.returnType)) {
|
| + _recordStaticType(node.methodName, inferredType);
|
| + _recordStaticType(node, inferredType.returnType);
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| + * Given a local variable declaration and its initializer, attempt to infer
|
| + * a type for the local variable declaration based on the initializer.
|
| + * Inference is only done if an explicit type is not present, and if
|
| + * inferring a type improves the type.
|
| + */
|
| + void _inferLocalVariableType(
|
| + VariableDeclaration node, Expression initializer) {
|
| + if (initializer != null &&
|
| + (node.parent as VariableDeclarationList).type == null &&
|
| + (node.element is LocalVariableElementImpl) &&
|
| + (initializer.staticType != null) &&
|
| + (!initializer.staticType.isBottom)) {
|
| + LocalVariableElementImpl element = node.element;
|
| + element.type = initializer.staticType;
|
| + node.name.staticType = initializer.staticType;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Given a property access [node] with static type [nodeType],
|
| + * and [id] is the property name being accessed, infer a type for the
|
| + * access itself and its constituent components if the access is to one of the
|
| + * methods or getters of the built in 'Object' type, and if the result type is
|
| + * a sealed type. Returns true if inference succeeded.
|
| + */
|
| + bool _inferObjectAccess(
|
| + Expression node, DartType nodeType, SimpleIdentifier id) {
|
| + // If we have an access like `libraryPrefix.hashCode` don't infer it.
|
| + if (node is PrefixedIdentifier &&
|
| + node.prefix.staticElement is PrefixElement) {
|
| + return false;
|
| + }
|
| + // Search for Object accesses.
|
| + String name = id.name;
|
| + PropertyAccessorElement inferredElement =
|
| + _typeProvider.objectType.element.getGetter(name);
|
| + if (inferredElement == null || inferredElement.isStatic) {
|
| + return false;
|
| + }
|
| + DartType inferredType = inferredElement.type.returnType;
|
| + if (nodeType != null &&
|
| + nodeType.isDynamic &&
|
| + inferredType != null &&
|
| + _typeProvider.nonSubtypableTypes.contains(inferredType)) {
|
| + _recordStaticType(id, inferredType);
|
| + _recordStaticType(node, inferredType);
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| * Return `true` if the given [Type] is the `Future` form the 'dart:async'
|
| * library.
|
| */
|
| @@ -1648,6 +1889,91 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
|
| }
|
|
|
| /**
|
| + * Return a more specialized type for a method invocation based on
|
| + * an ad-hoc list of pseudo-generic methods.
|
| + */
|
| + DartType _matchGeneric(MethodInvocation node) {
|
| + Element e = node.methodName.staticElement;
|
| +
|
| + if (e == null || e.name == null) {
|
| + return null;
|
| + }
|
| +
|
| + List<DartType> arguments =
|
| + node.argumentList.arguments.map((arg) => arg.staticType).toList();
|
| +
|
| + bool matchInvocation(DartType t, int c) {
|
| + return (node.realTarget != null) &&
|
| + node.realTarget.staticType.isSubtypeOf(t) &&
|
| + arguments.length == c;
|
| + }
|
| +
|
| + switch (e.name) {
|
| + case 'max':
|
| + case 'min':
|
| + if (e.library.source.uri.toString() == 'dart:math' &&
|
| + arguments.length == 2) {
|
| + DartType tx = arguments[0];
|
| + DartType ty = arguments[1];
|
| + if (tx == ty &&
|
| + (tx == _typeProvider.intType || tx == _typeProvider.doubleType)) {
|
| + return tx;
|
| + }
|
| + }
|
| + return null;
|
| + case 'wait':
|
| + if (matchInvocation(_typeProvider.futureType, 1)) {
|
| + DartType tx = arguments[0];
|
| + // Iterable<Future<T>> -> Future<List<T>>
|
| + DartType futureType =
|
| + _findIteratedType(tx, _typeProvider.iterableType);
|
| + if (futureType.element != _typeProvider.futureType.element) {
|
| + return null;
|
| + }
|
| + List<DartType> typeArguments =
|
| + (futureType as InterfaceType).typeArguments;
|
| + if (typeArguments.length != 1) {
|
| + return null;
|
| + }
|
| + DartType baseType = typeArguments[0];
|
| + if (baseType.isDynamic) {
|
| + return null;
|
| + }
|
| + return _typeProvider.futureType.substitute4([
|
| + _typeProvider.listType.substitute4([baseType])
|
| + ]);
|
| + }
|
| + return null;
|
| + case 'map':
|
| + if (matchInvocation(_typeProvider.iterableDynamicType, 1)) {
|
| + DartType tx = arguments[0];
|
| + return (tx is FunctionType)
|
| + ? _typeProvider.iterableType.substitute4([tx.returnType])
|
| + : null;
|
| + }
|
| + return null;
|
| + case 'fold':
|
| + if (matchInvocation(_typeProvider.iterableDynamicType, 2)) {
|
| + DartType tx = arguments[0];
|
| + DartType ty = arguments[1];
|
| + // TODO(vsm): LUB?
|
| + return (ty is FunctionType && tx == ty.returnType) ? tx : null;
|
| + }
|
| + return null;
|
| + case 'then':
|
| + if (matchInvocation(_typeProvider.futureDynamicType, 1)) {
|
| + DartType tx = arguments[0];
|
| + return (tx is FunctionType)
|
| + ? _typeProvider.futureType.substitute4([tx.returnType])
|
| + : null;
|
| + }
|
| + return null;
|
| + default:
|
| + return null;
|
| + }
|
| + }
|
| +
|
| + /**
|
| * Record that the propagated type of the given node is the given type.
|
| *
|
| * @param expression the node whose type is to be recorded
|
| @@ -1941,10 +2267,12 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
|
|
|
| class _StaticTypeAnalyzer_computePropagatedReturnTypeOfFunction
|
| extends GeneralizingAstVisitor<Object> {
|
| - final TypeSystem typeSystem;
|
| + final TypeSystem _typeSystem;
|
| + final TypeProvider _typeProvider;
|
| DartType result = null;
|
|
|
| - _StaticTypeAnalyzer_computePropagatedReturnTypeOfFunction(this.typeSystem);
|
| + _StaticTypeAnalyzer_computePropagatedReturnTypeOfFunction(
|
| + this._typeProvider, this._typeSystem);
|
|
|
| @override
|
| Object visitExpression(Expression node) => null;
|
| @@ -1963,7 +2291,7 @@ class _StaticTypeAnalyzer_computePropagatedReturnTypeOfFunction
|
| if (result == null) {
|
| result = type;
|
| } else {
|
| - result = typeSystem.getLeastUpperBound(result, type);
|
| + result = _typeSystem.getLeastUpperBound(_typeProvider, result, type);
|
| }
|
| return null;
|
| }
|
|
|