blob: d4597a87bad046db7fbe068cc60abe571fc4bfa8 [file] [log] [blame]
// 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:_fe_analyzer_shared/src/types/shared_type.dart';
import 'package:analyzer/dart/ast/token.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/element/type_system.dart';
import 'package:analyzer/src/dart/resolver/assignment_expression_resolver.dart';
import 'package:analyzer/src/dart/resolver/typed_literal_resolver.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/inference_log.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;
TypeSystemImpl get _typeSystem => _resolver.typeSystem;
void resolveElement(ForElementImpl node, CollectionLiteralContext? context) {
var forLoopParts = node.forLoopParts;
void visitBody() {
node.body.resolveElement(_resolver, context);
_resolver.popRewrite();
}
if (forLoopParts is ForPartsImpl) {
_forParts(node, forLoopParts, visitBody);
} else if (forLoopParts is ForEachPartsWithPatternImpl) {
_analyzePatternForIn(
node: node,
awaitKeyword: node.awaitKeyword,
forLoopParts: forLoopParts,
dispatchBody: () {
_resolver.dispatchCollectionElement(node.body, context);
},
);
} else if (forLoopParts is ForEachPartsImpl) {
_forEachParts(node, node.awaitKeyword != null, forLoopParts, visitBody);
}
}
void resolveStatement(ForStatementImpl node) {
var forLoopParts = node.forLoopParts;
void visitBody() {
node.body.accept(_resolver);
}
if (forLoopParts is ForPartsImpl) {
_forParts(node, forLoopParts, visitBody);
} else if (forLoopParts is ForEachPartsWithPatternImpl) {
_analyzePatternForIn(
node: node,
awaitKeyword: node.awaitKeyword,
forLoopParts: forLoopParts,
dispatchBody: () {
_resolver.dispatchStatement(node.body);
},
);
} else if (forLoopParts is ForEachPartsImpl) {
_forEachParts(node, node.awaitKeyword != null, forLoopParts, visitBody);
}
}
void _analyzePatternForIn({
required AstNodeImpl node,
required Token? awaitKeyword,
required ForEachPartsWithPatternImpl forLoopParts,
required void Function() dispatchBody,
}) {
_resolver.analyzePatternForIn(
node: node,
hasAwait: awaitKeyword != null,
pattern: forLoopParts.pattern,
expression: forLoopParts.iterable,
dispatchBody: dispatchBody,
);
_resolver.popRewrite();
_resolver.nullableDereferenceVerifier.expression(
CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_AS_ITERATOR,
forLoopParts.iterable,
);
}
/// 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.
TypeImpl _computeForEachElementType(ExpressionImpl iterable, bool isAsync) {
var iterableType = iterable.staticType;
if (iterableType == null) {
return InvalidTypeImpl.instance;
}
iterableType = _typeSystem.resolveToBound(iterableType);
if (iterableType is DynamicType) {
return DynamicTypeImpl.instance;
}
ClassElement iteratedElement =
isAsync
? _resolver.typeProvider.streamElement
: _resolver.typeProvider.iterableElement;
var iteratedType = iterableType.asInstanceOf(iteratedElement);
if (iteratedType == null) {
return InvalidTypeImpl.instance;
}
return iteratedType.typeArguments.single;
}
void _forEachParts(
AstNodeImpl node,
bool isAsync,
ForEachPartsImpl forEachParts,
void Function() visitBody,
) {
ExpressionImpl iterable = forEachParts.iterable;
DeclaredIdentifierImpl? loopVariable;
SimpleIdentifierImpl? identifier;
Element? identifierElement;
if (forEachParts is ForEachPartsWithDeclarationImpl) {
loopVariable = forEachParts.loopVariable;
} else if (forEachParts is ForEachPartsWithIdentifierImpl) {
identifier = forEachParts.identifier;
// TODO(scheglov): replace with lexical lookup
inferenceLogWriter?.setExpressionVisitCodePath(
identifier,
ExpressionVisitCodePath.forEachIdentifier,
);
identifier.accept(_resolver);
AssignmentExpressionShared(
resolver: _resolver,
).checkFinalAlreadyAssigned(identifier, isForEachIdentifier: true);
}
TypeImpl? valueType;
if (loopVariable != null) {
var typeAnnotation = loopVariable.type;
valueType = typeAnnotation?.type ?? UnknownInferredType.instance;
}
if (identifier != null) {
identifierElement = identifier.element;
if (identifierElement is VariableElement) {
valueType = _resolver.localVariableTypeProvider.getType(
identifier,
isRead: false,
);
} else if (identifierElement is InternalSetterElement) {
var parameters = identifierElement.formalParameters;
if (parameters.isNotEmpty) {
valueType = parameters[0].type;
}
}
}
InterfaceTypeImpl? targetType;
if (valueType != null) {
targetType =
isAsync
? _resolver.typeProvider.streamType(valueType)
: _resolver.typeProvider.iterableType(valueType);
}
_resolver.analyzeExpression(
iterable,
SharedTypeSchemaView(targetType ?? UnknownInferredType.instance),
);
iterable = _resolver.popRewrite()!;
_resolver.nullableDereferenceVerifier.expression(
CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_AS_ITERATOR,
iterable,
);
loopVariable?.accept(_resolver);
var elementType = _computeForEachElementType(iterable, isAsync);
if (loopVariable != null && loopVariable.type == null) {
var loopVariableElement =
loopVariable.declaredFragment?.element as LocalVariableElementImpl;
loopVariableElement.type = elementType;
}
if (loopVariable != null) {
var declaredElement = loopVariable.declaredElement!;
_resolver.flowAnalysis.flow?.declare(
declaredElement,
SharedTypeView(declaredElement.type),
initialized: true,
);
}
_resolver.flowAnalysis.flow?.forEach_bodyBegin(node);
if (identifierElement is PromotableElementImpl &&
forEachParts is ForEachPartsWithIdentifier) {
_resolver.flowAnalysis.flow?.write(
forEachParts,
identifierElement,
SharedTypeView(elementType),
null,
);
}
visitBody();
_resolver.flowAnalysis.flow?.forEach_end();
}
void _forParts(
AstNodeImpl node,
ForPartsImpl forParts,
void Function() visitBody,
) {
if (forParts is ForPartsWithDeclarationsImpl) {
forParts.variables.accept(_resolver);
} else if (forParts is ForPartsWithExpressionImpl) {
if (forParts.initialization case var initialization?) {
_resolver.analyzeExpression(
initialization,
_resolver.operations.unknownType,
);
_resolver.popRewrite();
}
} else if (forParts is ForPartsWithPatternImpl) {
forParts.variables.accept(_resolver);
} else {
throw StateError('Unrecognized for loop parts');
}
_resolver.flowAnalysis.for_conditionBegin(node);
var condition = forParts.condition;
if (condition != null) {
_resolver.analyzeExpression(
condition,
SharedTypeSchemaView(_resolver.typeProvider.boolType),
);
condition = _resolver.popRewrite()!;
var whyNotPromoted = _resolver.flowAnalysis.flow?.whyNotPromoted(
condition,
);
_resolver.boolExpressionVerifier.checkForNonBoolCondition(
condition,
whyNotPromoted: whyNotPromoted,
);
}
var deadCodeForPartsState =
_resolver.nullSafetyDeadCodeVerifier.for_conditionEnd();
_resolver.flowAnalysis.for_bodyBegin(node, condition);
visitBody();
_resolver.flowAnalysis.flow?.for_updaterBegin();
_resolver.nullSafetyDeadCodeVerifier.for_updaterBegin(
forParts.updaters,
deadCodeForPartsState,
);
for (var updater in forParts.updaters) {
_resolver.analyzeExpression(updater, _resolver.operations.unknownType);
_resolver.popRewrite();
}
_resolver.flowAnalysis.flow?.for_end();
}
}