| // Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'package:analyzer/dart/ast/ast.dart'; | |
| import 'package:analyzer/dart/element/element.dart'; | |
| import 'package:analyzer/dart/element/type.dart'; | |
| import 'package:analyzer/src/dart/ast/ast.dart'; | |
| import 'package:analyzer/src/dart/element/element.dart'; | |
| import 'package:analyzer/src/dart/element/type.dart'; | |
| import 'package:analyzer/src/dart/element/type_system.dart'; | |
| import 'package:analyzer/src/dart/resolver/body_inference_context.dart'; | |
| import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart'; | |
| import 'package:analyzer/src/generated/migration.dart'; | |
| import 'package:analyzer/src/generated/resolver.dart'; | |
| import 'package:collection/collection.dart'; | |
| class FunctionExpressionResolver { | |
| final ResolverVisitor _resolver; | |
| final MigrationResolutionHooks? _migrationResolutionHooks; | |
| final InvocationInferenceHelper _inferenceHelper; | |
| FunctionExpressionResolver({ | |
| required ResolverVisitor resolver, | |
| required MigrationResolutionHooks? migrationResolutionHooks, | |
| }) : _resolver = resolver, | |
| _migrationResolutionHooks = migrationResolutionHooks, | |
| _inferenceHelper = resolver.inferenceHelper; | |
| bool get _isNonNullableByDefault => _typeSystem.isNonNullableByDefault; | |
| TypeSystemImpl get _typeSystem => _resolver.typeSystem; | |
| void resolve(FunctionExpressionImpl node) { | |
| var parent = node.parent; | |
| // Note: `isFunctionDeclaration` must have an explicit type to work around | |
| // https://github.com/dart-lang/language/issues/1785. | |
| bool isFunctionDeclaration = parent is FunctionDeclaration; | |
| var body = node.body; | |
| if (_resolver.flowAnalysis!.flow != null && !isFunctionDeclaration) { | |
| _resolver.flowAnalysis! | |
| .executableDeclaration_enter(node, node.parameters, true); | |
| } | |
| var contextType = InferenceContext.getContext(node); | |
| if (contextType is FunctionType) { | |
| contextType = _matchTypeParameters( | |
| node.typeParameters, | |
| contextType, | |
| ); | |
| if (contextType is FunctionType) { | |
| _inferFormalParameters(node.parameters, contextType); | |
| InferenceContext.setType(body, contextType.returnType); | |
| } | |
| } | |
| node.visitChildren(_resolver); | |
| if (isFunctionDeclaration) { | |
| // A side effect of visiting the children is that the parameters are now | |
| // in scope, so we can visit the documentation comment now. | |
| parent.documentationComment?.accept(_resolver); | |
| } | |
| _resolve2(node); | |
| if (_resolver.flowAnalysis!.flow != null && !isFunctionDeclaration) { | |
| var bodyContext = BodyInferenceContext.of(node.body); | |
| _resolver.checkForBodyMayCompleteNormally( | |
| returnType: bodyContext?.contextType, | |
| body: body, | |
| errorNode: body, | |
| ); | |
| _resolver.flowAnalysis!.flow?.functionExpression_end(); | |
| _resolver.nullSafetyDeadCodeVerifier.flowEnd(node); | |
| } | |
| } | |
| /// Infer types of implicitly typed formal parameters. | |
| void _inferFormalParameters( | |
| FormalParameterList? node, | |
| FunctionType contextType, | |
| ) { | |
| if (node == null) { | |
| return; | |
| } | |
| void inferType(ParameterElementImpl p, DartType inferredType) { | |
| // Check that there is no declared type, and that we have not already | |
| // inferred a type in some fashion. | |
| if (p.hasImplicitType && p.type.isDynamic) { | |
| // If no type is declared for a parameter and there is a | |
| // corresponding parameter in the context type schema with type | |
| // schema `K`, the parameter is given an inferred type `T` where `T` | |
| // is derived from `K` as follows. | |
| inferredType = _typeSystem.greatestClosureOfSchema(inferredType); | |
| // If the greatest closure of `K` is `S` and `S` is a subtype of | |
| // `Null`, then `T` is `Object?`. Otherwise, `T` is `S`. | |
| if (_typeSystem.isSubtypeOf(inferredType, _typeSystem.nullNone)) { | |
| inferredType = _isNonNullableByDefault | |
| ? _typeSystem.objectQuestion | |
| : _typeSystem.objectStar; | |
| } | |
| if (_migrationResolutionHooks != null) { | |
| inferredType = _migrationResolutionHooks! | |
| .modifyInferredParameterType(p, inferredType); | |
| } else { | |
| inferredType = _typeSystem.nonNullifyLegacy(inferredType); | |
| } | |
| if (!inferredType.isDynamic) { | |
| p.type = inferredType; | |
| } | |
| } | |
| } | |
| var parameters = node.parameterElements.whereNotNull(); | |
| { | |
| Iterator<ParameterElement> positional = | |
| parameters.where((p) => p.isPositional).iterator; | |
| Iterator<ParameterElement> fnPositional = | |
| contextType.parameters.where((p) => p.isPositional).iterator; | |
| while (positional.moveNext() && fnPositional.moveNext()) { | |
| inferType(positional.current as ParameterElementImpl, | |
| fnPositional.current.type); | |
| } | |
| } | |
| { | |
| Map<String, DartType> namedParameterTypes = | |
| contextType.namedParameterTypes; | |
| Iterable<ParameterElement> named = parameters.where((p) => p.isNamed); | |
| for (var p in named) { | |
| if (!namedParameterTypes.containsKey(p.name)) { | |
| continue; | |
| } | |
| inferType(p as ParameterElementImpl, namedParameterTypes[p.name]!); | |
| } | |
| } | |
| } | |
| /// Infers the return type of a local function, either a lambda or | |
| /// (in strong mode) a local function declaration. | |
| DartType _inferLocalFunctionReturnType(FunctionExpression node) { | |
| var body = node.body; | |
| return InferenceContext.getContext(body) ?? DynamicTypeImpl.instance; | |
| } | |
| /// Given the downward inference [type], return the function type expressed | |
| /// in terms of the type parameters from [typeParameterList]. | |
| /// | |
| /// Return `null` is the number of element in [typeParameterList] is not | |
| /// the same as the number of type parameters in the [type]. | |
| FunctionType? _matchTypeParameters( | |
| TypeParameterList? typeParameterList, FunctionType type) { | |
| if (typeParameterList == null) { | |
| if (type.typeFormals.isEmpty) { | |
| return type; | |
| } | |
| return null; | |
| } | |
| var typeParameters = typeParameterList.typeParameters; | |
| if (typeParameters.length != type.typeFormals.length) { | |
| return null; | |
| } | |
| return type.instantiate(typeParameters.map((typeParameter) { | |
| return typeParameter.declaredElement!.instantiate( | |
| nullabilitySuffix: _resolver.noneOrStarSuffix, | |
| ); | |
| }).toList()); | |
| } | |
| void _resolve2(FunctionExpressionImpl node) { | |
| var functionElement = node.declaredElement as ExecutableElementImpl; | |
| if (_shouldUpdateReturnType(node)) { | |
| var returnType = _inferLocalFunctionReturnType(node); | |
| functionElement.returnType = returnType; | |
| } | |
| _inferenceHelper.recordStaticType(node, functionElement.type); | |
| } | |
| static bool _shouldUpdateReturnType(FunctionExpression node) { | |
| var parent = node.parent; | |
| if (parent is FunctionDeclaration) { | |
| // Local function without declared return type. | |
| return parent.parent is FunctionDeclarationStatement && | |
| parent.returnType == null; | |
| } else { | |
| // Pure function expression. | |
| return true; | |
| } | |
| } | |
| } |