|  | // 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_schema.dart'; | 
|  | import 'package:analyzer/src/dart/resolver/assignment_expression_resolver.dart'; | 
|  | import 'package:analyzer/src/error/codes.dart'; | 
|  | import 'package:analyzer/src/generated/resolver.dart'; | 
|  |  | 
|  | /// Helper for resolving [ForStatement]s and [ForElement]s. | 
|  | class ForResolver { | 
|  | final ResolverVisitor _resolver; | 
|  |  | 
|  | ForResolver({ | 
|  | required ResolverVisitor resolver, | 
|  | }) : _resolver = resolver; | 
|  |  | 
|  | void resolveElement(ForElementImpl node) { | 
|  | var forLoopParts = node.forLoopParts; | 
|  | if (forLoopParts is ForPartsImpl) { | 
|  | _forParts(node, forLoopParts, node.body); | 
|  | } else if (forLoopParts is ForEachPartsImpl) { | 
|  | _forEachParts(node, node.awaitKeyword != null, forLoopParts, node.body); | 
|  | } | 
|  | } | 
|  |  | 
|  | void resolveStatement(ForStatementImpl node) { | 
|  | _resolver.checkUnreachableNode(node); | 
|  |  | 
|  | var forLoopParts = node.forLoopParts; | 
|  | if (forLoopParts is ForPartsImpl) { | 
|  | _forParts(node, forLoopParts, node.body); | 
|  | } else if (forLoopParts is ForEachPartsImpl) { | 
|  | _forEachParts(node, node.awaitKeyword != null, forLoopParts, node.body); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Given an iterable expression from a foreach loop, attempt to infer | 
|  | /// a type for the elements being iterated over.  Inference is based | 
|  | /// on the type of the iterator or stream over which the foreach loop | 
|  | /// is defined. | 
|  | DartType? _computeForEachElementType(Expression iterable, bool isAsync) { | 
|  | var iterableType = iterable.staticType; | 
|  | if (iterableType == null) return null; | 
|  | iterableType = | 
|  | iterableType.resolveToBound(_resolver.typeProvider.objectType); | 
|  |  | 
|  | ClassElement iteratedElement = isAsync | 
|  | ? _resolver.typeProvider.streamElement | 
|  | : _resolver.typeProvider.iterableElement; | 
|  |  | 
|  | var iteratedType = iterableType.asInstanceOf(iteratedElement); | 
|  |  | 
|  | if (iteratedType != null) { | 
|  | var elementType = iteratedType.typeArguments.single; | 
|  | elementType = _resolver.toLegacyTypeIfOptOut(elementType); | 
|  | return elementType; | 
|  | } else { | 
|  | return null; | 
|  | } | 
|  | } | 
|  |  | 
|  | void _forEachParts( | 
|  | AstNode node, | 
|  | bool isAsync, | 
|  | ForEachParts forEachParts, | 
|  | AstNode body, | 
|  | ) { | 
|  | Expression iterable = forEachParts.iterable; | 
|  | DeclaredIdentifier? loopVariable; | 
|  | SimpleIdentifier? identifier; | 
|  | Element? identifierElement; | 
|  | if (forEachParts is ForEachPartsWithDeclaration) { | 
|  | loopVariable = forEachParts.loopVariable; | 
|  | } else if (forEachParts is ForEachPartsWithIdentifier) { | 
|  | identifier = forEachParts.identifier; | 
|  | // TODO(scheglov) replace with lexical lookup | 
|  | identifier.accept(_resolver); | 
|  | AssignmentExpressionShared( | 
|  | resolver: _resolver, | 
|  | ).checkFinalAlreadyAssigned(identifier); | 
|  | } | 
|  |  | 
|  | DartType? valueType; | 
|  | if (loopVariable != null) { | 
|  | var typeAnnotation = loopVariable.type; | 
|  | valueType = typeAnnotation?.type ?? UnknownInferredType.instance; | 
|  | } | 
|  | if (identifier != null) { | 
|  | identifierElement = identifier.staticElement; | 
|  | if (identifierElement is VariableElement) { | 
|  | valueType = _resolver.localVariableTypeProvider | 
|  | .getType(identifier, isRead: false); | 
|  | } else if (identifierElement is PropertyAccessorElement) { | 
|  | var parameters = identifierElement.parameters; | 
|  | if (parameters.isNotEmpty) { | 
|  | valueType = parameters[0].type; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (valueType != null) { | 
|  | InterfaceType targetType = isAsync | 
|  | ? _resolver.typeProvider.streamType(valueType) | 
|  | : _resolver.typeProvider.iterableType(valueType); | 
|  | InferenceContext.setType(iterable, targetType); | 
|  | } | 
|  |  | 
|  | iterable.accept(_resolver); | 
|  | iterable = forEachParts.iterable; | 
|  |  | 
|  | _resolver.nullableDereferenceVerifier.expression( | 
|  | CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_AS_ITERATOR, | 
|  | iterable, | 
|  | ); | 
|  |  | 
|  | loopVariable?.accept(_resolver); | 
|  | var elementType = _computeForEachElementType(iterable, isAsync); | 
|  | if (loopVariable != null && | 
|  | elementType != null && | 
|  | loopVariable.type == null) { | 
|  | var loopVariableElement = | 
|  | loopVariable.declaredElement as LocalVariableElementImpl; | 
|  | loopVariableElement.type = elementType; | 
|  | } | 
|  |  | 
|  | if (loopVariable != null) { | 
|  | _resolver.flowAnalysis.flow?.declare(loopVariable.declaredElement!, true); | 
|  | } | 
|  |  | 
|  | _resolver.flowAnalysis.flow?.forEach_bodyBegin(node); | 
|  | if (identifierElement is PromotableElement && | 
|  | forEachParts is ForEachPartsWithIdentifier) { | 
|  | _resolver.flowAnalysis.flow?.write(forEachParts, identifierElement, | 
|  | elementType ?? DynamicTypeImpl.instance, null); | 
|  | } | 
|  |  | 
|  | body.accept(_resolver); | 
|  |  | 
|  | _resolver.flowAnalysis.flow?.forEach_end(); | 
|  | } | 
|  |  | 
|  | void _forParts(AstNode node, ForParts forParts, AstNode body) { | 
|  | if (forParts is ForPartsWithDeclarations) { | 
|  | forParts.variables.accept(_resolver); | 
|  | } else if (forParts is ForPartsWithExpression) { | 
|  | forParts.initialization?.accept(_resolver); | 
|  | } | 
|  |  | 
|  | _resolver.flowAnalysis.for_conditionBegin(node); | 
|  |  | 
|  | var condition = forParts.condition; | 
|  | if (condition != null) { | 
|  | InferenceContext.setType(condition, _resolver.typeProvider.boolType); | 
|  | condition.accept(_resolver); | 
|  | condition = forParts.condition!; | 
|  | var whyNotPromoted = | 
|  | _resolver.flowAnalysis.flow?.whyNotPromoted(condition); | 
|  | _resolver.boolExpressionVerifier | 
|  | .checkForNonBoolCondition(condition, whyNotPromoted: whyNotPromoted); | 
|  | } | 
|  |  | 
|  | _resolver.flowAnalysis.for_bodyBegin(node, condition); | 
|  | body.accept(_resolver); | 
|  |  | 
|  | _resolver.flowAnalysis.flow?.for_updaterBegin(); | 
|  | forParts.updaters.accept(_resolver); | 
|  |  | 
|  | _resolver.flowAnalysis.flow?.for_end(); | 
|  | } | 
|  | } |