Index: pkg/analyzer/lib/src/fasta/resolution_storer.dart |
diff --git a/pkg/analyzer/lib/src/fasta/resolution_storer.dart b/pkg/analyzer/lib/src/fasta/resolution_storer.dart |
index 00f95333c5bd1aa294cc82e235bacac89bedb5ba..c39447ea93e8a0e3f7b1dc726160ec2178d29794 100644 |
--- a/pkg/analyzer/lib/src/fasta/resolution_storer.dart |
+++ b/pkg/analyzer/lib/src/fasta/resolution_storer.dart |
@@ -17,13 +17,13 @@ class InstrumentedResolutionStorer extends ResolutionStorer { |
: super(types); |
@override |
- void _recordType(DartType type, int offset) { |
+ int _recordType(DartType type, int offset) { |
if (_debug) { |
print('Recording type $type for offset $offset'); |
} |
assert(_types.length == _typeOffsets.length); |
_typeOffsets.add(offset); |
- super._recordType(type, offset); |
+ return super._recordType(type, offset); |
} |
} |
@@ -32,8 +32,16 @@ class InstrumentedResolutionStorer extends ResolutionStorer { |
class ResolutionStorer extends TypeInferenceListener { |
final List<DartType> _types; |
+ /// Indices into [_types] which need to be filled in later. |
+ final _deferredTypeSlots = <int>[]; |
+ |
ResolutionStorer(this._types); |
+ /// Verifies that all deferred work has been completed. |
+ void finished() { |
+ assert(_deferredTypeSlots.isEmpty); |
+ } |
+ |
@override |
bool genericExpressionEnter( |
String expressionType, Expression expression, DartType typeContext) { |
@@ -48,13 +56,76 @@ class ResolutionStorer extends TypeInferenceListener { |
super.genericExpressionExit(expressionType, expression, inferredType); |
} |
+ @override |
+ void methodInvocationBeforeArgs(Expression expression, bool isImplicitCall) { |
+ if (!isImplicitCall) { |
+ // We are visiting a method invocation like: a.f(args). We have visited a |
+ // but we haven't visited the args yet. |
+ // |
+ // The analyzer AST will expect a type for f at this point. (It can't |
+ // wait until later, because for all it knows, a.f might be a property |
+ // access, in which case the appropriate time for the type is now). But |
+ // the type isn't known yet (because it may depend on type inference based |
+ // on arguments). |
+ // |
+ // So we add a `null` to our list of types; we'll update it with the |
+ // actual type later. |
+ _deferredTypeSlots.add(_recordType(null, expression.fileOffset)); |
+ } |
+ super.methodInvocationBeforeArgs(expression, isImplicitCall); |
+ } |
+ |
+ @override |
+ void methodInvocationExit(Expression expression, Arguments arguments, |
+ bool isImplicitCall, DartType inferredType) { |
+ if (!isImplicitCall) { |
+ // TODO(paulberry): get the actual callee function type from the inference |
+ // engine |
+ var calleeType = const DynamicType(); |
+ _types[_deferredTypeSlots.removeLast()] = calleeType; |
+ } |
+ _recordType(inferredType, arguments.fileOffset); |
+ super.genericExpressionExit("methodInvocation", expression, inferredType); |
+ } |
+ |
+ @override |
+ bool staticInvocationEnter( |
+ StaticInvocation expression, DartType typeContext) { |
+ // We are visiting a static invocation like: f(args), and we haven't visited |
+ // args yet. |
+ // |
+ // The analyzer AST will expect a type for f at this point. (It can't wait |
+ // until later, because for all it knows, f is a method on `this`, and |
+ // methods need a type for f at this point--see comments in |
+ // [methodInvocationBeforeArgs]). But the type isn't known yet (because it |
+ // may depend on type inference based on arguments). |
+ // |
+ // So we add a `null` to our list of types; we'll update it with the actual |
+ // type later. |
+ _deferredTypeSlots.add(_recordType(null, expression.fileOffset)); |
+ return super.staticInvocationEnter(expression, typeContext); |
+ } |
+ |
+ @override |
+ void staticInvocationExit( |
+ StaticInvocation expression, DartType inferredType) { |
+ // TODO(paulberry): get the actual callee function type from the inference |
+ // engine |
+ var calleeType = const DynamicType(); |
+ _types[_deferredTypeSlots.removeLast()] = calleeType; |
+ _recordType(inferredType, expression.arguments.fileOffset); |
+ super.genericExpressionExit("staticInvocation", expression, inferredType); |
+ } |
+ |
@override |
void variableDeclarationEnter(VariableDeclaration statement) { |
_recordType(statement.type, statement.fileOffset); |
super.variableDeclarationEnter(statement); |
} |
- void _recordType(DartType type, int offset) { |
+ int _recordType(DartType type, int offset) { |
+ int slot = _types.length; |
_types.add(type); |
+ return slot; |
} |
} |