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

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

Issue 1462133005: Downwards inference. This adds support to the resolver for downwards (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Minor fixes Created 5 years, 1 month 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/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].

Powered by Google App Engine
This is Rietveld 408576698