Chromium Code Reviews| 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]. |