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

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

Issue 1352763004: Add additional strong mode inference. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Add tests Created 5 years, 3 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
Index: pkg/analyzer/lib/src/generated/static_type_analyzer.dart
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
index aea9874f160988e572cb0f5795f41a38950535ce..b2011dd64e40cfaa1b1d84c609ec09f24989f929 100644
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/pkg/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;
@@ -84,6 +89,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
_dynamicType = _typeProvider.dynamicType;
_overrideManager = _resolver.overrideManager;
_promoteManager = _resolver.promoteManager;
+ _strongMode = _resolver.definingLibrary.context.analysisOptions.strongMode;
}
/**
@@ -306,7 +312,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
@override
Object visitDeclaredIdentifier(DeclaredIdentifier node) {
super.visitDeclaredIdentifier(node);
- if (_resolver.definingLibrary.context.analysisOptions.strongMode) {
+ if (_strongMode) {
_inferForEachLoopVariableType(node);
}
return null;
@@ -619,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) {
@@ -883,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].
@@ -1012,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) {
@@ -1195,7 +1215,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
@override
Object visitVariableDeclaration(VariableDeclaration node) {
Expression initializer = node.initializer;
- if (_resolver.definingLibrary.context.analysisOptions.strongMode) {
+ if (_strongMode) {
_inferLocalVariableType(node, initializer);
}
if (initializer != null) {
@@ -1656,6 +1676,12 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
return returnType;
}
+ /**
+ * 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 &&
@@ -1679,6 +1705,81 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
}
}
+ /**
+ * 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?
Jennifer Messerly 2015/09/22 18:17:21 long line
Leaf 2015/09/22 18:37:28 Done.
+ 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') {
+ DartType returnType = _getFirstArgumentAsType(
+ _typeProvider.objectType.element.library, node.argumentList);
+ if (returnType != null) {
+ _recordPropagatedType(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) {
+ // Object methods called on dynamic targets can have their types improved.
+ String name = node.methodName.name;
+ DartType inferredType = _typeProvider.objectMemberTypes[name];
+ DartType nodeType = node.staticType;
+ if (nodeType != null &&
+ nodeType.isDynamic &&
+ inferredType is FunctionType &&
+ inferredType.parameters.isEmpty &&
+ node.argumentList.arguments.isEmpty &&
+ _typeProvider.nonSubtypableTypes.contains(inferredType.returnType)) {
+ //TODO(leafp): When we start marking dynamic calls for the backend, be
+ // sure that this does not get marked as dynamic.
+ _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 &&
@@ -1693,6 +1794,32 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
}
/**
+ * Given a property access [node], where [target] is the target of the access
+ * 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) {
+ // Search for Object accesses.
+ String name = id.name;
+ DartType inferredType = _typeProvider.objectMemberTypes[name];
+ if (nodeType != null &&
+ nodeType.isDynamic &&
+ inferredType != null &&
+ _typeProvider.nonSubtypableTypes.contains(inferredType)) {
+ // TODO(leafp): Eliminate the dynamic call here once we start
+ // annotating dynamic calls from this code. Even if the type is not
+ // sealed we can eliminate the dynamic call.
+ _recordStaticType(id, inferredType);
+ _recordStaticType(node, inferredType);
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Return `true` if the given [Type] is the `Future` form the 'dart:async'
* library.
*/
@@ -1737,6 +1864,91 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
}
/**
+ * Return a more specialized type for a method invocation based on
+ * an ad-hoc list of pseudo-generic methids.
+ */
+ 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

Powered by Google App Engine
This is Rietveld 408576698