| 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 194d87690f72eebe2ab12d7c5ea863d3e573599f..aee5c5ac282727299d1ec63000415331d711f5b2 100644
|
| --- a/pkg/analyzer/lib/src/generated/resolver.dart
|
| +++ b/pkg/analyzer/lib/src/generated/resolver.dart
|
| @@ -4697,8 +4697,6 @@ class InferenceContext {
|
| * A stack of return types for all of the enclosing
|
| * functions and methods.
|
| */
|
| - // TODO(leafp) Handle the implicit union type for Futures
|
| - // https://github.com/dart-lang/sdk/issues/25322
|
| final List<DartType> _returnStack = <DartType>[];
|
|
|
| InferenceContext._(this._errorReporter, TypeProvider typeProvider,
|
| @@ -4727,9 +4725,12 @@ class InferenceContext {
|
| if (_returnStack.isEmpty) {
|
| return;
|
| }
|
| - DartType inferred = _inferredReturn.last;
|
| - inferred = _typeSystem.getLeastUpperBound(_typeProvider, type, inferred);
|
| - _inferredReturn[_inferredReturn.length - 1] = inferred;
|
| + DartType context = _returnStack.last;
|
| + if (context is! FutureUnionType) {
|
| + DartType inferred = _inferredReturn.last;
|
| + inferred = _typeSystem.getLeastUpperBound(_typeProvider, type, inferred);
|
| + _inferredReturn[_inferredReturn.length - 1] = inferred;
|
| + }
|
| }
|
|
|
| /**
|
| @@ -4766,8 +4767,7 @@ class InferenceContext {
|
| * Push a block function body's return type onto the return stack.
|
| */
|
| void pushReturnContext(BlockFunctionBody node) {
|
| - DartType returnType = getType(node);
|
| - _returnStack.add(returnType);
|
| + _returnStack.add(getContext(node));
|
| _inferredReturn.add(BottomTypeImpl.instance);
|
| }
|
|
|
| @@ -4909,18 +4909,49 @@ class InferenceContext {
|
| }
|
|
|
| /**
|
| + * Look for a single contextual type attached to [node], and returns the type
|
| + * if found, otherwise null.
|
| + *
|
| + * If [node] has a contextual union type like `T | Future<T>` this will
|
| + * simplify it to only return `T`. If the caller can handle a union type,
|
| + * [getContext] should be used instead.
|
| + */
|
| + static DartType getType(AstNode node) {
|
| + DartType t = getContext(node);
|
| + if (t is FutureUnionType) {
|
| + return t.type;
|
| + }
|
| + return t;
|
| + }
|
| +
|
| + /**
|
| * Look for contextual type information attached to [node]. Returns
|
| * the type if found, otherwise null.
|
| + *
|
| + * If [node] has a contextual union type like `T | Future<T>` this will be
|
| + * returned. You can use [getType] if you prefer to only get the `T`.
|
| */
|
| - static DartType getType(AstNode node) => node?.getProperty(_typeProperty);
|
| + static DartType getContext(AstNode node) => node?.getProperty(_typeProperty);
|
| +
|
| + /**
|
| + * Like [getContext] but expands a union type into a list of types.
|
| + */
|
| + static Iterable<DartType> getTypes(AstNode node) {
|
| + DartType t = getContext(node);
|
| + if (t == null) {
|
| + return DartType.EMPTY_LIST;
|
| + }
|
| + if (t is FutureUnionType) {
|
| + return t.types;
|
| + }
|
| + return <DartType>[t];
|
| + }
|
|
|
| /**
|
| * Attach contextual type information [type] to [node] for use during
|
| * inference.
|
| */
|
| static void setType(AstNode node, DartType type) {
|
| - // TODO(jmesserly): this sets the type even when it's dynamic.
|
| - // Can we skip that?
|
| node?.setProperty(_typeProperty, type);
|
| }
|
|
|
| @@ -4929,7 +4960,7 @@ class InferenceContext {
|
| * inference.
|
| */
|
| static void setTypeFromNode(AstNode innerNode, AstNode outerNode) {
|
| - setType(innerNode, getType(outerNode));
|
| + setType(innerNode, getContext(outerNode));
|
| }
|
| }
|
|
|
| @@ -6013,13 +6044,11 @@ class ResolverVisitor extends ScopedVisitor {
|
|
|
| @override
|
| Object visitAwaitExpression(AwaitExpression node) {
|
| - // TODO(leafp): Handle the implicit union type here
|
| - // https://github.com/dart-lang/sdk/issues/25322
|
| - DartType contextType = InferenceContext.getType(node);
|
| + DartType contextType = InferenceContext.getContext(node);
|
| if (contextType != null) {
|
| - InterfaceType futureT = typeProvider.futureType
|
| - .instantiate([contextType.flattenFutures(typeSystem)]);
|
| - InferenceContext.setType(node.expression, futureT);
|
| + var futureUnion =
|
| + FutureUnionType.from(contextType, typeProvider, typeSystem);
|
| + InferenceContext.setType(node.expression, futureUnion);
|
| }
|
| return super.visitAwaitExpression(node);
|
| }
|
| @@ -6073,7 +6102,7 @@ class ResolverVisitor extends ScopedVisitor {
|
| if (operatorType == TokenType.QUESTION_QUESTION) {
|
| // Set the right side, either from the context, or using the information
|
| // from the left side if it is more precise.
|
| - DartType contextType = InferenceContext.getType(node);
|
| + DartType contextType = InferenceContext.getContext(node);
|
| DartType leftType = leftOperand?.staticType;
|
| if (contextType == null || contextType.isDynamic) {
|
| contextType = leftType;
|
| @@ -6551,8 +6580,24 @@ class ResolverVisitor extends ScopedVisitor {
|
| matchFunctionTypeParameters(node.typeParameters, functionType);
|
| if (functionType is FunctionType) {
|
| _inferFormalParameterList(node.parameters, functionType);
|
| - DartType returnType =
|
| - _computeReturnOrYieldType(functionType.returnType);
|
| +
|
| + DartType returnType;
|
| + if (_isFutureThenLambda(node)) {
|
| + var futureThenType =
|
| + InferenceContext.getContext(node.parent) as FunctionType;
|
| +
|
| + // Pretend the return type of Future<T>.then<S> first parameter is
|
| + //
|
| + // T -> (S | Future<S>)
|
| + //
|
| + // We can't represent this in Dart so we populate it here during
|
| + // inference.
|
| + returnType = FutureUnionType.from(
|
| + futureThenType.returnType, typeProvider, typeSystem);
|
| + } else {
|
| + returnType = _computeReturnOrYieldType(functionType.returnType);
|
| + }
|
| +
|
| InferenceContext.setType(node.body, returnType);
|
| }
|
| }
|
| @@ -6668,30 +6713,37 @@ class ResolverVisitor extends ScopedVisitor {
|
| Object visitInstanceCreationExpression(InstanceCreationExpression node) {
|
| TypeName classTypeName = node.constructorName.type;
|
| if (classTypeName.typeArguments == null) {
|
| - DartType contextType = InferenceContext.getType(node);
|
| - if (contextType is InterfaceType &&
|
| - contextType.typeArguments != null &&
|
| - contextType.typeArguments.length > 0) {
|
| - // TODO(jmesserly): for generic methods we use the
|
| - // StrongTypeSystemImpl.inferGenericFunctionCall, which appears to
|
| - // be a tad more powerful than matchTypes.
|
| - //
|
| - // For example it can infer this case:
|
| - //
|
| - // class E<S, T> extends A<C<S>, T> { ... }
|
| - // A<C<int>, String> a0 = /*infer<int, String>*/new E("hello");
|
| - //
|
| - // See _inferArgumentTypesFromContext in this file for use of it.
|
| - 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);
|
| + // Given a union of context types ` T0 | T1 | ... | Tn`, find the first
|
| + // valid instantiation `new C<Ti>`, if it exists.
|
| + // TODO(jmesserly): if we support union types for real, `new C<Ti | Tj>`
|
| + // will become a valid possibility. Right now the only allowed union is
|
| + // `T | Future<T>` so we can take a simple approach.
|
| + for (var contextType in InferenceContext.getTypes(node)) {
|
| + if (contextType is InterfaceType &&
|
| + contextType.typeArguments != null &&
|
| + contextType.typeArguments.isNotEmpty) {
|
| + // TODO(jmesserly): for generic methods we use the
|
| + // StrongTypeSystemImpl.inferGenericFunctionCall, which appears to
|
| + // be a tad more powerful than matchTypes.
|
| + //
|
| + // For example it can infer this case:
|
| + //
|
| + // class E<S, T> extends A<C<S>, T> { ... }
|
| + // A<C<int>, String> a0 = /*infer<int, String>*/new E("hello");
|
| + //
|
| + // See _inferArgumentTypesFromContext in this file for use of it.
|
| + 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);
|
| + break;
|
| + }
|
| }
|
| }
|
| }
|
| @@ -6805,7 +6857,7 @@ class ResolverVisitor extends ScopedVisitor {
|
|
|
| @override
|
| Object visitNamedExpression(NamedExpression node) {
|
| - InferenceContext.setType(node.expression, InferenceContext.getType(node));
|
| + InferenceContext.setTypeFromNode(node.expression, node);
|
| return super.visitNamedExpression(node);
|
| }
|
|
|
| @@ -6819,7 +6871,7 @@ class ResolverVisitor extends ScopedVisitor {
|
|
|
| @override
|
| Object visitParenthesizedExpression(ParenthesizedExpression node) {
|
| - InferenceContext.setType(node.expression, InferenceContext.getType(node));
|
| + InferenceContext.setTypeFromNode(node.expression, node);
|
| return super.visitParenthesizedExpression(node);
|
| }
|
|
|
| @@ -6937,7 +6989,7 @@ class ResolverVisitor extends ScopedVisitor {
|
|
|
| @override
|
| Object visitVariableDeclaration(VariableDeclaration node) {
|
| - InferenceContext.setType(node.initializer, InferenceContext.getType(node));
|
| + InferenceContext.setTypeFromNode(node.initializer, node);
|
| super.visitVariableDeclaration(node);
|
| VariableElement element = node.element;
|
| if (element.initializer != null && node.initializer != null) {
|
| @@ -7081,21 +7133,22 @@ class ResolverVisitor extends ScopedVisitor {
|
| if (!isGenerator && !isAsynchronous) {
|
| return declaredType;
|
| }
|
| - if (isGenerator) {
|
| - if (declaredType is! InterfaceType) {
|
| - return null;
|
| + if (declaredType is InterfaceType) {
|
| + if (isGenerator) {
|
| + // If it's sync* we expect Iterable<T>
|
| + // If it's async* we expect Stream<T>
|
| + InterfaceType rawType = isAsynchronous
|
| + ? typeProvider.streamDynamicType
|
| + : typeProvider.iterableDynamicType;
|
| + // Match the types to instantiate the type arguments if possible
|
| + List<DartType> typeArgs =
|
| + inferenceContext.matchTypes(rawType, declaredType);
|
| + return (typeArgs?.length == 1) ? typeArgs[0] : null;
|
| }
|
| - // If it's synchronous, we expect Iterable<T>, otherwise Stream<T>
|
| - InterfaceType rawType = isAsynchronous
|
| - ? typeProvider.streamDynamicType
|
| - : typeProvider.iterableDynamicType;
|
| - // Match the types to instantiate the type arguments if possible
|
| - List<DartType> typeArgs =
|
| - inferenceContext.matchTypes(rawType, declaredType);
|
| - return (typeArgs?.length == 1) ? typeArgs[0] : null;
|
| + // async functions expect `Future<T> | T`
|
| + return new FutureUnionType(declaredType, typeProvider, typeSystem);
|
| }
|
| - // Must be asynchronous to reach here, so strip off any layers of Future
|
| - return declaredType.flattenFutures(typeSystem);
|
| + return declaredType;
|
| }
|
|
|
| /**
|
| @@ -7340,6 +7393,19 @@ class ResolverVisitor extends ScopedVisitor {
|
| }
|
|
|
| /**
|
| + * Returns true if this expression is being passed to `Future.then`.
|
| + *
|
| + * If so we will apply special typing rules in strong mode, to handle the
|
| + * implicit union of `S | Future<S>`
|
| + */
|
| + bool _isFutureThenLambda(FunctionExpression node) {
|
| + Element element = node.staticParameterElement?.enclosingElement;
|
| + return element is MethodElement &&
|
| + element.name == 'then' &&
|
| + element.enclosingElement.type.isDartAsyncFuture;
|
| + }
|
| +
|
| + /**
|
| * Return `true` if the given variable is accessed within a closure in the given
|
| * [AstNode] and also mutated somewhere in variable scope. This information is only
|
| * available for local variables (including parameters).
|
|
|