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 ad1cd606e01da266f2beca48b84da5c9a984955c..397949937131840720d9c0d571839b44f4acf961 100644 |
--- a/pkg/analyzer/lib/src/generated/resolver.dart |
+++ b/pkg/analyzer/lib/src/generated/resolver.dart |
@@ -6,6 +6,8 @@ library engine.resolver; |
import 'dart:collection'; |
+import '../task/strong/info.dart' show InferredType, StaticInfo; |
+import '../task/strong/rules.dart' show TypeRules; |
import 'ast.dart'; |
import 'constant.dart'; |
import 'element.dart'; |
@@ -5754,6 +5756,193 @@ class ImportsVerifier /*extends RecursiveAstVisitor<Object>*/ { |
} |
} |
+/** Maintains and manages contextual type information used for |
Brian Wilkerson
2015/11/21 16:12:22
Not asking for a change, but I'm not a fan of the
Jennifer Messerly
2015/11/23 22:11:57
Yeah same here. I've been trying to follow:
/**
Leaf
2015/12/01 21:49:11
Done.
Brian Wilkerson
2015/12/01 22:31:09
Personally, I find the "///" style hard to read, s
Bob Nystrom
2015/12/01 23:33:27
I'll point out two things:
1. The style guide req
Leaf
2015/12/01 23:39:24
Per point 2: that's my fault (caught in the review
|
+ * inferring types. |
+ */ |
+class InferenceContext { |
Jennifer Messerly
2015/11/23 22:11:57
"Context" as a class suffix often just means "here
Leaf
2015/12/01 21:49:11
Acknowledged.
|
+ /** The key by which we hang information off of ast nodes. |
+ */ |
+ static const String _propertyName = |
Jennifer Messerly
2015/11/23 22:11:57
might be worth giving this a more descriptive name
Leaf
2015/12/01 21:49:11
Done.
|
+ 'analyzer.src.generated.InferenceContext.contextType'; |
+ |
+ /** The error listener on which to record inference information. |
+ */ |
+ final AnalysisErrorListener _errorListener; |
+ |
+ /** Type provider, needed for type matching. |
+ */ |
+ final TypeProvider _typeProvider; |
+ |
+ /** The type system in use. |
+ */ |
+ final TypeSystem _typeSystem; |
+ |
+ /** The DDC type rules, used to create the inference info nodes. |
+ */ |
+ final TypeRules _rules; |
Jennifer Messerly
2015/11/23 22:11:57
Question: are we still on track to remove this obj
Leaf
2015/11/24 19:32:12
Yes. It's here only to produce the info nodes tha
|
+ |
+ /** A stack of return types for all of the enclosing |
+ * functions and methods. |
+ */ |
+ List<DartType> _returnStack = <DartType>[]; |
+ |
+ InferenceContext( |
Jennifer Messerly
2015/11/23 22:11:58
BTW, if either the type or constructor can be made
Leaf
2015/11/24 19:32:11
The type has to be public since it's accessed by t
Jennifer Messerly
2015/11/24 20:10:57
InferenceContext._(...)
Leaf
2015/12/01 21:49:11
Done.
|
+ this._errorListener, TypeProvider typeProvider, this._typeSystem) |
+ : this._typeProvider = typeProvider, |
Jennifer Messerly
2015/11/23 22:11:57
nit: "this." is not necessary here
Leaf
2015/12/01 21:49:10
Done.
|
+ this._rules = new TypeRules(typeProvider); |
+ |
+ /** Get the return type of the current enclusing function, if any. |
Brian Wilkerson
2015/11/21 16:12:22
"enclusing" --> "enclosing"
Leaf
2015/12/01 21:49:11
Done.
|
+ */ |
+ DartType get returnContext => |
+ (_returnStack.isNotEmpty) ? _returnStack.last : null; |
+ |
+ /** If t1 = I<dynamic, ..., dynamic>, then look for a supertype |
Jennifer Messerly
2015/11/23 22:11:57
BTW: you probably want to put backticks around gen
Leaf
2015/12/01 21:49:11
Done.
|
+ * of t1 of the form K<S0, ..., Sm> where t2 = K<S0', ..., Sm'> |
Brian Wilkerson
2015/11/21 16:12:22
What is 't2'?
Jennifer Messerly
2015/11/23 22:11:57
looks like it's the second argument to matchTypes
Leaf
2015/12/01 21:49:11
Added a line to the comments to make this clearer.
|
+ * If the supertype exists, use the constraints S0 <: S0', ... Sm <: Sm' |
+ * to derive a concrete instantation for I of the form <T0, ..., Tn>, |
+ * such that I<T0, .., Tn> <: t2 |
+ */ |
+ List<DartType> matchTypes(DartType t1, DartType t2) => |
+ (t1 is InterfaceType && t2 is InterfaceType) ? _matchTypes(t1, t2) : null; |
+ |
+ /** Pop a return type off of the return stack. |
+ */ |
+ void popReturnContext() { |
+ if (_returnStack.isNotEmpty) { |
Brian Wilkerson
2015/11/21 16:12:22
Add "assert(_returnStack.isNotEmpty);" before the
Leaf
2015/12/01 21:49:11
Done.
|
+ _returnStack.removeLast(); |
+ } |
+ } |
+ |
+ /** Push a [returnType] onto the return stack. |
+ */ |
+ void pushReturnContext(DartType returnType) { |
+ _returnStack.add(returnType); |
+ } |
+ |
+ /** Place an info node into the error stream indicating that a |
+ * [type] has been inferred as the type of [node]. |
+ */ |
+ void recordInference(Expression node, DartType type) { |
+ StaticInfo info = InferredType.create(_rules, node, type); |
+ if (info == null) return; |
Brian Wilkerson
2015/11/21 16:12:22
Stylistic nit (for here and elsewhere): we always
Leaf
2015/11/24 19:32:12
Sorry - pulled over from DDC code and I missed the
Leaf
2015/12/01 21:49:11
Done.
|
+ var error = info.toAnalysisError(); |
+ _errorListener.onError(error); |
+ } |
+ |
+ List<DartType> _matchTypes(InterfaceType t1, InterfaceType t2) { |
+ if (t1 == t2) { |
+ return t2.typeArguments; |
+ } |
+ List<DartType> tArgs1 = t1.typeArguments; |
+ List<DartType> tArgs2 = t2.typeArguments; |
+ // If t1 isn't a raw type, bail out |
+ if (tArgs1 != null && tArgs1.any((t) => !t.isDynamic)) { |
+ return null; |
+ } |
+ |
+ // This is our inferred type argument list. We start at all dynamic, |
+ // and fill in with inferred types when we reach a match. |
+ List<DartType> actuals = |
+ new List<DartType>.filled(tArgs1.length, _typeProvider.dynamicType); |
+ |
+ // When we find the supertype of t1 with the same |
+ // classname as t2 (see below), we have the following: |
+ // If t1 is an instantiation of a class T1<X0, ..., Xn> |
+ // and t2 is an instantiation of a class T2<Y0, ...., Ym> |
+ // of the form t2 = T2<S0, ..., Sm> |
+ // then we want to choose instantiations for the Xi |
+ // T0, ..., Tn such that T1<T0, ..., Tn> <: t2 . |
+ // To find this, we simply instantate T1 with |
+ // X0, ..., Xn, and then find its superclass |
+ // T2<T0', ..., Tn'>. We then solve the constraint |
+ // set T0' <: S0, ..., Tn' <: Sn for the Xi. |
+ // Currently, we only handle constraints where |
+ // the Ti' is one of the Xi'. If there are multiple |
+ // constraints on some Xi, we choose the lower of the |
+ // two (if it exists). |
+ bool permute(List<DartType> permutedArgs) { |
+ if (permutedArgs == null) { |
+ return false; |
+ } |
+ List<TypeParameterElement> ps = t1.typeParameters; |
+ List<DartType> ts = ps.map((p) => p.type).toList(); |
+ for (int i = 0; i < permutedArgs.length; i++) { |
+ DartType tVar = permutedArgs[i]; |
+ DartType tActual = tArgs2[i]; |
+ int index = ts.indexOf(tVar); |
+ if (index >= 0 && _typeSystem.isSubtypeOf(tActual, actuals[index])) { |
+ actuals[index] = tActual; |
+ } |
+ } |
+ return actuals.any((x) => !x.isDynamic); |
+ } |
+ |
+ // Look for the first supertype of t1 with the same class name as t2. |
+ bool match(InterfaceType t1) { |
Brian Wilkerson
2015/11/21 16:12:22
This method will result in an infinite loop if the
Leaf
2015/12/01 21:49:11
Done.
|
+ if (t1.element == t2.element) { |
+ return permute(t1.typeArguments); |
+ } |
+ |
+ if (t1 == _typeProvider.objectType) { |
+ return false; |
+ } |
+ |
+ if (match(t1.superclass)) { |
+ return true; |
+ } |
+ |
+ for (final parent in t1.interfaces) { |
+ if (match(parent)) { |
+ return true; |
+ } |
+ } |
+ |
+ for (final parent in t1.mixins) { |
Brian Wilkerson
2015/11/21 16:12:22
Interesting that we do mixins after interfaces. I
Jennifer Messerly
2015/11/23 22:11:58
Yeah. I would normally think of mixins as coming b
Leaf
2015/11/24 19:32:11
I need to sit down and work through the details of
|
+ if (match(parent)) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ // We have that t1 = T1<dynamic, ..., dynamic>. |
+ // To match t1 against t2, we use the uninstantiated version |
+ // of t1, essentially treating it as an instantiation with |
+ // fresh variables, and solve for the variables. |
+ // t1.element.type will be of the form T1<X0, ..., Xn> |
+ if (!match(t1.element.type)) return null; |
+ DartType newT1 = t1.element.type.substitute4(actuals); |
+ // If we found a solution, return it. |
+ if (_typeSystem.isSubtypeOf(newT1, t2)) return actuals; |
+ return null; |
+ } |
+ |
+ /** Attach contextual type information [type] to [node] for use during |
+ * inference. |
+ */ |
+ static void annotateNode(AstNode node, DartType type) { |
Jennifer Messerly
2015/11/23 22:11:58
(note: the following may be obsolete in light of m
Leaf
2015/12/01 21:49:10
Done.
|
+ node?.setProperty(_propertyName, type); |
Jennifer Messerly
2015/11/23 22:11:57
hmmmm. Thinking on this more (having seen the use
Leaf
2015/11/24 19:32:12
Yeah, I think this would be fine. I initially did
Brian Wilkerson
2015/11/24 21:17:57
I don't have a problem with either approach.
The
Leaf
2015/12/01 21:49:11
I'm leaving it as it is for now, but added a todo
|
+ return; |
Jennifer Messerly
2015/11/23 22:11:57
nit: return not needed here
Leaf
2015/12/01 21:49:10
The visitor methods seem to always explicitly retu
|
+ } |
+ |
+ /** Clear the type information assocated with [node]. |
+ */ |
+ static void clear(AstNode node) { |
+ node?.setProperty(_propertyName, null); |
+ return; |
+ } |
+ |
+ // If t1 = I<dynamic, ..., dynamic>, then look for a supertype |
Jennifer Messerly
2015/11/23 22:11:57
Is this supposed to be here? Consider folding it i
Leaf
2015/12/01 21:49:10
Done.
|
+ // of t1 of the form K<S0, ..., Sm> where t2 = K<S0', ..., Sm'> |
+ // If the supertype exists, use the constraints S0 <: S0', ... Sm <: Sm' |
+ // to derive a concrete instantation for I of the form <T0, ..., Tn>, |
+ // such that I<T0, .., Tn> <: t2 |
+ /** Look for contextual type information attached to [node]. Returns |
+ * the type if found, otherwise null. |
+ */ |
+ static DartType get(AstNode node) => node?.getProperty(_propertyName); |
+} |
+ |
/** |
* Instances of the class `InheritanceManager` manage the knowledge of where class members |
* (methods, getters & setters) are inherited from. |
@@ -10372,8 +10561,8 @@ class ResolverVisitor extends ScopedVisitor { |
StaticTypeAnalyzer typeAnalyzer; |
/* |
- * The type system in use during resolution. |
- */ |
+ * The type system in use during resolution. |
+ */ |
TypeSystem typeSystem; |
/** |
@@ -10406,6 +10595,8 @@ class ResolverVisitor extends ScopedVisitor { |
*/ |
Comment _commentBeforeFunction = null; |
+ InferenceContext inferenceContext = null; |
Jennifer Messerly
2015/11/23 22:11:58
hmmm, I guess this is for consistency, but `= null
Leaf
2015/12/01 21:49:11
Acknowledged.
|
+ |
/** |
* The object keeping track of which elements have had their types overridden. |
*/ |
@@ -10457,6 +10648,8 @@ class ResolverVisitor extends ScopedVisitor { |
} |
this.elementResolver = new ElementResolver(this); |
this.typeSystem = definingLibrary.context.typeSystem; |
+ this.inferenceContext = |
+ new InferenceContext(errorListener, typeProvider, typeSystem); |
if (typeAnalyzerFactory == null) { |
this.typeAnalyzer = new StaticTypeAnalyzer(this); |
} else { |
@@ -10729,11 +10922,63 @@ class ResolverVisitor extends ScopedVisitor { |
identical(parent, _enclosingFunctionTypeAlias)) { |
return null; |
} |
- return super.visitAnnotation(node); |
+ safelyVisit(node.name); |
+ safelyVisit(node.constructorName); |
+ Element element = node.element; |
+ if (element is ExecutableElement) { |
+ InferenceContext.annotateNode(node.arguments, element.type); |
+ } |
+ safelyVisit(node.arguments); |
+ node.accept(elementResolver); |
+ node.accept(typeAnalyzer); |
+ return null; |
+ } |
+ |
+ @override |
+ Object visitArgumentList(ArgumentList node) { |
+ DartType callerType = InferenceContext.get(node); |
+ if (callerType is FunctionType) { |
+ Map<String, DartType> namedParameterTypes = |
+ callerType.namedParameterTypes; |
+ List<DartType> normalParameterTypes = callerType.normalParameterTypes; |
+ List<DartType> optionalParameterTypes = callerType.optionalParameterTypes; |
+ int normalCount = normalParameterTypes.length; |
+ int optionalCount = optionalParameterTypes.length; |
+ int namedCount = namedParameterTypes.length; |
+ |
+ NodeList<Expression> arguments = node.arguments; |
+ Iterable<Expression> positional = |
+ arguments.takeWhile((l) => l is! NamedExpression); |
+ Iterable<Expression> required = positional.take(normalCount); |
+ Iterable<Expression> optional = |
+ positional.skip(normalCount).take(optionalCount); |
+ Iterable<Expression> named = |
+ arguments.skipWhile((l) => l is! NamedExpression); |
+ |
+ int index = 0; |
Jennifer Messerly
2015/11/23 22:11:57
trivial: I'd probably just write this as a C-style
Leaf
2015/11/24 19:32:12
The for loop version above will throw an out of bo
Jennifer Messerly
2015/11/24 20:10:58
Ah, in that case, probably best to leave it as is!
|
+ for (Expression argument in required) { |
+ InferenceContext.annotateNode(argument, normalParameterTypes[index++]); |
Jennifer Messerly
2015/11/23 22:11:58
you might be able to avoid some of the above logic
Leaf
2015/12/01 21:49:11
Maybe? I'd have to think through the various impl
|
+ } |
+ index = 0; |
+ for (Expression argument in optional) { |
+ InferenceContext.annotateNode( |
+ argument, optionalParameterTypes[index++]); |
+ } |
+ |
+ for (Expression argument in named) { |
+ if (argument is NamedExpression && |
+ namedParameterTypes.containsKey(argument.name.label.name)) { |
Jennifer Messerly
2015/11/23 22:11:57
not that it matters, but I'd be tempted to rewrite
Leaf
2015/12/01 21:49:11
Done.
|
+ InferenceContext.annotateNode( |
+ argument, namedParameterTypes[argument.name.label.name]); |
+ } |
+ } |
+ } |
+ super.visitArgumentList(node); |
Brian Wilkerson
2015/11/21 16:12:22
Missing return
Leaf
2015/12/01 21:49:10
Done.
|
} |
@override |
Object visitAsExpression(AsExpression node) { |
+ InferenceContext.annotateNode(node.expression, node.type.type); |
super.visitAsExpression(node); |
// Since an as-statement doesn't actually change the type, we don't |
// let it affect the propagated type when it would result in a loss |
@@ -10750,6 +10995,34 @@ class ResolverVisitor extends ScopedVisitor { |
} |
@override |
+ Object visitAssignmentExpression(AssignmentExpression node) { |
+ safelyVisit(node.leftHandSide); |
+ sc.TokenType operator = node.operator.type; |
+ if (operator == sc.TokenType.EQ || |
+ operator == sc.TokenType.QUESTION_QUESTION_EQ) { |
+ InferenceContext.annotateNode( |
+ node.rightHandSide, node.leftHandSide.staticType); |
+ } |
+ safelyVisit(node.rightHandSide); |
+ node.accept(elementResolver); |
+ node.accept(typeAnalyzer); |
+ return null; |
+ } |
+ |
+ @override |
+ Object visitAwaitExpression(AwaitExpression node) { |
+ //TODO(leafp): Handle the implicit union type here |
+ DartType contextType = InferenceContext.get(node); |
+ if (contextType != null) { |
+ InterfaceType futureT = |
+ typeProvider.futureType.substitute4([contextType]); |
+ InferenceContext.annotateNode(node.expression, futureT); |
+ } |
+ super.visitAwaitExpression(node); |
+ return null; |
Brian Wilkerson
2015/11/21 16:12:22
Or "return super.visitAwaitExpression(node);"
Leaf
2015/12/01 21:49:10
Done.
|
+ } |
+ |
+ @override |
Object visitBinaryExpression(BinaryExpression node) { |
sc.TokenType operatorType = node.operator.type; |
Expression leftOperand = node.leftOperand; |
@@ -10789,6 +11062,7 @@ class ResolverVisitor extends ScopedVisitor { |
} |
} |
} else { |
+ // TODO(leafp): Handle this case |
Brian Wilkerson
2015/11/21 16:12:22
I won't remember what this means outside the conte
Leaf
2015/12/01 21:49:11
Done.
|
safelyVisit(leftOperand); |
safelyVisit(rightOperand); |
} |
@@ -10802,9 +11076,11 @@ class ResolverVisitor extends ScopedVisitor { |
safelyVisit(_commentBeforeFunction); |
_overrideManager.enterScope(); |
try { |
+ inferenceContext.pushReturnContext(InferenceContext.get(node)); |
super.visitBlockFunctionBody(node); |
} finally { |
_overrideManager.exitScope(); |
+ inferenceContext.popReturnContext(); |
} |
return null; |
} |
@@ -10821,6 +11097,12 @@ class ResolverVisitor extends ScopedVisitor { |
} |
@override |
+ Object visitCascadeExpression(CascadeExpression node) { |
+ InferenceContext.annotateNode(node.target, InferenceContext.get(node)); |
+ super.visitCascadeExpression(node); |
+ } |
+ |
+ @override |
Object visitClassDeclaration(ClassDeclaration node) { |
// |
// Resolve the metadata in the library scope. |
@@ -10953,6 +11235,8 @@ class ResolverVisitor extends ScopedVisitor { |
_clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated( |
thenExpression); |
// Visit "then" expression. |
+ InferenceContext.annotateNode( |
Jennifer Messerly
2015/11/23 22:11:57
might be worth a helper for this?
InferenceCo
Leaf
2015/12/01 21:49:11
Done.
|
+ thenExpression, InferenceContext.get(node)); |
thenExpression.accept(this); |
} finally { |
_promoteManager.exitScope(); |
@@ -10966,6 +11250,8 @@ class ResolverVisitor extends ScopedVisitor { |
_overrideManager.enterScope(); |
try { |
_propagateFalseState(condition); |
+ InferenceContext.annotateNode( |
+ elseExpression, InferenceContext.get(node)); |
elseExpression.accept(this); |
} finally { |
_overrideManager.exitScope(); |
@@ -10990,6 +11276,11 @@ class ResolverVisitor extends ScopedVisitor { |
ExecutableElement outerFunction = _enclosingFunction; |
try { |
_enclosingFunction = node.element; |
+ DartType type = _enclosingFunction.type; |
+ if (type is FunctionType) { |
Jennifer Messerly
2015/11/23 22:11:58
I think this would always be the case for a constr
Leaf
2015/12/01 21:49:11
Done.
|
+ _inferFormalParameterList(node.parameters, type); |
Jennifer Messerly
2015/11/23 22:11:57
was just chatting about this--I'm not sure this co
Leaf
2015/12/01 21:49:11
Done.
|
+ InferenceContext.annotateNode(node.body, type.returnType); |
+ } |
super.visitConstructorDeclaration(node); |
} finally { |
_enclosingFunction = outerFunction; |
@@ -11006,6 +11297,8 @@ class ResolverVisitor extends ScopedVisitor { |
// We visit the expression, but do not visit the field name because it needs |
// to be visited in the context of the constructor field initializer node. |
// |
+ FieldElement fieldElement = enclosingClass.getField(node.fieldName.name); |
Jennifer Messerly
2015/11/23 22:11:57
Does `fieldName.staticElement` give you the field
Leaf
2015/11/24 19:32:12
Not set until the elementResolver runs below. I co
Jennifer Messerly
2015/11/24 20:10:57
ah, interesting. In this case I think re-ordering
Leaf
2015/12/01 21:49:11
Yes, it's fine here and now, but I feel like every
|
+ InferenceContext.annotateNode(node.expression, fieldElement?.type); |
safelyVisit(node.expression); |
node.accept(elementResolver); |
node.accept(typeAnalyzer); |
@@ -11037,6 +11330,8 @@ class ResolverVisitor extends ScopedVisitor { |
@override |
Object visitDefaultFormalParameter(DefaultFormalParameter node) { |
+ InferenceContext.annotateNode( |
+ node.defaultValue, node.parameter.element?.type); |
super.visitDefaultFormalParameter(node); |
ParameterElement element = node.element; |
if (element.initializer != null && node.defaultValue != null) { |
@@ -11115,6 +11410,8 @@ class ResolverVisitor extends ScopedVisitor { |
} |
_overrideManager.enterScope(); |
try { |
+ InferenceContext.annotateNode( |
+ node.expression, InferenceContext.get(node)); |
super.visitExpressionFunctionBody(node); |
} finally { |
_overrideManager.exitScope(); |
@@ -11154,9 +11451,16 @@ class ResolverVisitor extends ScopedVisitor { |
// cannot be in scope while visiting the iterator. |
// |
Expression iterable = node.iterable; |
- safelyVisit(iterable); |
DeclaredIdentifier loopVariable = node.loopVariable; |
SimpleIdentifier identifier = node.identifier; |
+ if (loopVariable?.type?.type != null) { |
+ InterfaceType targetType = (node.awaitKeyword == null) |
+ ? typeProvider.iterableType |
+ : typeProvider.streamType; |
+ InferenceContext.annotateNode( |
+ iterable, targetType.substitute4([loopVariable.type.type])); |
+ } |
+ safelyVisit(iterable); |
safelyVisit(loopVariable); |
safelyVisit(identifier); |
Statement body = node.body; |
@@ -11229,6 +11533,8 @@ class ResolverVisitor extends ScopedVisitor { |
try { |
SimpleIdentifier functionName = node.name; |
_enclosingFunction = functionName.staticElement as ExecutableElement; |
+ InferenceContext.annotateNode( |
+ node.functionExpression, _enclosingFunction.type); |
super.visitFunctionDeclaration(node); |
} finally { |
_enclosingFunction = outerFunction; |
@@ -11243,6 +11549,11 @@ class ResolverVisitor extends ScopedVisitor { |
_enclosingFunction = node.element; |
_overrideManager.enterScope(); |
try { |
+ DartType functionType = InferenceContext.get(node); |
+ if (functionType is FunctionType) { |
+ _inferFormalParameterList(node.parameters, functionType); |
+ InferenceContext.annotateNode(node.body, functionType.returnType); |
+ } |
super.visitFunctionExpression(node); |
} finally { |
_overrideManager.exitScope(); |
@@ -11258,6 +11569,7 @@ class ResolverVisitor extends ScopedVisitor { |
safelyVisit(node.function); |
node.accept(elementResolver); |
_inferFunctionExpressionsParametersTypes(node.argumentList); |
+ InferenceContext.annotateNode(node.argumentList, node.function.staticType); |
safelyVisit(node.argumentList); |
node.accept(typeAnalyzer); |
return null; |
@@ -11345,16 +11657,107 @@ class ResolverVisitor extends ScopedVisitor { |
} |
@override |
+ Object visitInstanceCreationExpression(InstanceCreationExpression node) { |
+ DartType contextType = InferenceContext.get(node); |
+ if (contextType is InterfaceType && |
+ contextType.typeArguments != null && |
+ contextType.typeArguments.length > 0) { |
+ TypeName classTypeName = node.constructorName.type; |
+ if (classTypeName.typeArguments == null) { |
+ List<DartType> targs = |
+ inferenceContext.matchTypes(classTypeName.type, contextType); |
+ if (targs != null && targs.any((t) => !t.isDynamic)) { |
+ ClassElement classElement = classTypeName.type.element; |
+ InterfaceType rawType = classElement.type; |
+ InterfaceType fullType = |
+ rawType.substitute2(targs, rawType.typeArguments); |
+ // The element resolver uses the type on the constructor name, so |
+ // infer it first |
+ typeAnalyzer.inferConstructorName(node.constructorName, fullType); |
+ safelyVisit(node.constructorName); |
+ ConstructorElement invokedConstructor = |
+ node.constructorName.staticElement; |
+ FunctionType rawConstructorType = invokedConstructor.type; |
+ FunctionType constructorType = rawConstructorType.substitute2( |
+ targs, rawConstructorType.typeArguments); |
+ InferenceContext.annotateNode(node.argumentList, constructorType); |
+ safelyVisit(node.argumentList); |
+ InferenceContext.annotateNode(node, fullType); |
+ node.accept(elementResolver); |
+ node.accept(typeAnalyzer); |
+ return null; |
+ } |
+ } else { |
+ InferenceContext.clear(node); |
+ } |
+ } |
+ super.visitInstanceCreationExpression(node); |
+ return null; |
+ } |
+ |
+ @override |
Object visitLabel(Label node) => null; |
@override |
Object visitLibraryIdentifier(LibraryIdentifier node) => null; |
@override |
+ Object visitListLiteral(ListLiteral node) { |
+ DartType contextType = InferenceContext.get(node); |
+ if (node.typeArguments == null && contextType is InterfaceType) { |
+ InterfaceType listD = |
+ typeProvider.listType.substitute4([typeProvider.dynamicType]); |
+ List<DartType> targs = inferenceContext.matchTypes(listD, contextType); |
+ if (targs != null && |
+ targs.length == 1 && |
+ targs.any((t) => !t.isDynamic)) { |
+ DartType eType = targs[0]; |
+ InterfaceType listT = typeProvider.listType.substitute4([eType]); |
+ for (Expression child in node.elements) { |
+ InferenceContext.annotateNode(child, eType); |
+ } |
+ InferenceContext.annotateNode(node, listT); |
+ } else { |
+ InferenceContext.clear(node); |
+ } |
+ } |
+ super.visitListLiteral(node); |
+ return null; |
+ } |
+ |
+ @override |
+ Object visitMapLiteral(MapLiteral node) { |
+ DartType contextType = InferenceContext.get(node); |
+ if (node.typeArguments == null && contextType is InterfaceType) { |
+ InterfaceType mapD = typeProvider.mapType |
+ .substitute4([typeProvider.dynamicType, typeProvider.dynamicType]); |
+ List<DartType> targs = inferenceContext.matchTypes(mapD, contextType); |
+ if (targs != null && |
+ targs.length == 2 && |
+ targs.any((t) => !t.isDynamic)) { |
+ DartType kType = targs[0]; |
+ DartType vType = targs[1]; |
+ InterfaceType mapT = typeProvider.mapType.substitute4([kType, vType]); |
+ for (MapLiteralEntry entry in node.entries) { |
+ InferenceContext.annotateNode(entry.key, kType); |
+ InferenceContext.annotateNode(entry.value, vType); |
+ } |
+ InferenceContext.annotateNode(node, mapT); |
+ } else { |
+ InferenceContext.clear(node); |
+ } |
+ } |
+ super.visitMapLiteral(node); |
+ return null; |
+ } |
+ |
+ @override |
Object visitMethodDeclaration(MethodDeclaration node) { |
ExecutableElement outerFunction = _enclosingFunction; |
try { |
_enclosingFunction = node.element; |
+ _inferFormalParameterList(node.parameters, node.element.type); |
+ InferenceContext.annotateNode(node.body, node.element.type?.returnType); |
super.visitMethodDeclaration(node); |
} finally { |
_enclosingFunction = outerFunction; |
@@ -11371,12 +11774,22 @@ class ResolverVisitor extends ScopedVisitor { |
safelyVisit(node.target); |
node.accept(elementResolver); |
_inferFunctionExpressionsParametersTypes(node.argumentList); |
+ if (node.methodName.staticElement is ExecutableElement) { |
Jennifer Messerly
2015/11/23 22:11:57
suggestion, if you save this into a variable you c
Leaf
2015/12/01 21:49:10
Done.
|
+ DartType type = (node.methodName.staticElement as ExecutableElement).type; |
+ InferenceContext.annotateNode(node.argumentList, type); |
+ } |
safelyVisit(node.argumentList); |
node.accept(typeAnalyzer); |
return null; |
} |
@override |
+ Object visitNamedExpression(NamedExpression node) { |
+ InferenceContext.annotateNode(node.expression, InferenceContext.get(node)); |
+ super.visitNamedExpression(node); |
Jennifer Messerly
2015/11/23 22:11:57
not sure if Brian got tired of pointing it out ...
Leaf
2015/12/01 21:49:10
Done.
|
+ } |
+ |
+ @override |
Object visitNode(AstNode node) { |
node.visitChildren(this); |
node.accept(elementResolver); |
@@ -11385,6 +11798,12 @@ class ResolverVisitor extends ScopedVisitor { |
} |
@override |
+ Object visitParenthesizedExpression(ParenthesizedExpression node) { |
+ InferenceContext.annotateNode(node.expression, InferenceContext.get(node)); |
+ super.visitParenthesizedExpression(node); |
+ } |
+ |
+ @override |
Object visitPrefixedIdentifier(PrefixedIdentifier node) { |
// |
// We visit the prefix, but do not visit the identifier because it needs to |
@@ -11416,6 +11835,7 @@ class ResolverVisitor extends ScopedVisitor { |
// because it needs to be visited in the context of the constructor |
// invocation. |
// |
+ InferenceContext.annotateNode(node.argumentList, node.staticElement?.type); |
safelyVisit(node.argumentList); |
node.accept(elementResolver); |
node.accept(typeAnalyzer); |
@@ -11423,6 +11843,13 @@ class ResolverVisitor extends ScopedVisitor { |
} |
@override |
+ Object visitReturnStatement(ReturnStatement node) { |
+ InferenceContext.annotateNode( |
+ node.expression, inferenceContext.returnContext); |
+ super.visitReturnStatement(node); |
+ } |
+ |
+ @override |
Object visitShowCombinator(ShowCombinator node) => null; |
@override |
@@ -11432,6 +11859,7 @@ class ResolverVisitor extends ScopedVisitor { |
// because it needs to be visited in the context of the constructor |
// invocation. |
// |
+ InferenceContext.annotateNode(node.argumentList, node.staticElement?.type); |
safelyVisit(node.argumentList); |
node.accept(elementResolver); |
node.accept(typeAnalyzer); |
@@ -11479,6 +11907,7 @@ class ResolverVisitor extends ScopedVisitor { |
@override |
Object visitVariableDeclaration(VariableDeclaration node) { |
+ InferenceContext.annotateNode(node.initializer, InferenceContext.get(node)); |
super.visitVariableDeclaration(node); |
VariableElement element = node.element; |
if (element.initializer != null && node.initializer != null) { |
@@ -11500,6 +11929,13 @@ class ResolverVisitor extends ScopedVisitor { |
return null; |
} |
+ @override visitVariableDeclarationList(VariableDeclarationList node) { |
+ for (VariableDeclaration decl in node.variables) { |
+ InferenceContext.annotateNode(decl, node.type?.type); |
+ } |
+ super.visitVariableDeclarationList(node); |
+ } |
+ |
@override |
Object visitWhileStatement(WhileStatement node) { |
// Note: since we don't call the base class, we have to maintain |
@@ -11529,6 +11965,32 @@ class ResolverVisitor extends ScopedVisitor { |
return null; |
} |
+ @override |
+ Object visitYieldStatement(YieldStatement node) { |
+ DartType returnType = inferenceContext.returnContext; |
+ if (returnType != null && _enclosingFunction != null) { |
+ // If we're not in a generator ([a]sync*, then we shouldn't have a yield. |
+ // so don't infer |
+ if (_enclosingFunction.isGenerator) { |
+ // If this is a yield*, then we just propagate the return type downwards |
+ DartType type = returnType; |
+ // If this just a yield, then we need to get the element type |
+ if (node.star == null) { |
+ // If it's synchronous, we expect Iterable<T>, otherwise Stream<T> |
+ InterfaceType wrapperD = (_enclosingFunction.isSynchronous) |
Jennifer Messerly
2015/11/23 22:11:57
nit: parens not needed
Leaf
2015/12/01 21:49:11
Done.
|
+ ? typeProvider.iterableDynamicType |
+ : typeProvider.streamDynamicType; |
+ // Match the types to instantiate the type arguments if possible |
+ List<DartType> targs = |
+ inferenceContext.matchTypes(wrapperD, returnType); |
+ type = (targs?.length == 1) ? targs[0] : null; |
+ } |
+ InferenceContext.annotateNode(node.expression, type); |
+ } |
+ } |
+ super.visitYieldStatement(node); |
Brian Wilkerson
2015/11/21 16:12:22
Missing return
Leaf
2015/11/24 19:32:12
Just a meta-comment: this is one of those analyzer
Brian Wilkerson
2015/11/24 21:17:57
That depends on the IDE. I don't know of any way t
Leaf
2015/12/01 21:49:10
Done.
|
+ } |
+ |
/** |
* Checks each promoted variable in the current scope for compliance with the following |
* specification statement: |
@@ -11623,6 +12085,14 @@ class ResolverVisitor extends ScopedVisitor { |
return null; |
} |
+ void _inferFormalParameterList(FormalParameterList node, DartType type) { |
+ if (typeAnalyzer.inferFormalParameterList(node, type)) { |
+ // TODO(leafp): This gets dropped on the floor if we're in the field |
+ // inference task. We should probably keep these infos. |
+ inferenceContext.recordInference(node.parent, type); |
+ } |
+ } |
+ |
/** |
* If given "mayBeClosure" is [FunctionExpression] without explicit parameters types and its |
* required type is [FunctionType], then infer parameters types from [FunctionType]. |