blob: 4bc14b1ba84e4dd419e66be45723a2e887661122 [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/ast.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/element/type_provider.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/extensions.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/body_inference_context.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart';
/// Helper for resolving [YieldStatement]s.
class YieldStatementResolver {
final ResolverVisitor _resolver;
YieldStatementResolver({
required ResolverVisitor resolver,
}) : _resolver = resolver;
ErrorReporter get _errorReporter => _resolver.errorReporter;
TypeProvider get _typeProvider => _resolver.typeProvider;
TypeSystemImpl get _typeSystem => _resolver.typeSystem;
void resolve(YieldStatement node) {
var bodyContext = _resolver.bodyContext;
if (bodyContext != null && bodyContext.isGenerator) {
_resolve_generator(bodyContext, node);
} else {
_resolve_notGenerator(node);
}
}
/// Check for situations where the result of a method or function is used, when
/// it returns 'void'. Or, in rare cases, when other types of expressions are
/// void, such as identifiers.
///
/// See [CompileTimeErrorCode.USE_OF_VOID_RESULT].
///
// TODO(scheglov): This is duplicate
// TODO(scheglov): Also in [BoolExpressionVerifier]
bool _checkForUseOfVoidResult(Expression expression) {
if (!identical(expression.staticType, VoidTypeImpl.instance)) {
return false;
}
if (expression is MethodInvocation) {
_errorReporter.atNode(
expression.methodName,
CompileTimeErrorCode.USE_OF_VOID_RESULT,
);
} else {
_errorReporter.atNode(
expression,
CompileTimeErrorCode.USE_OF_VOID_RESULT,
);
}
return true;
}
/// Check for a type mis-match between the yielded type and the declared
/// return type of a generator function.
///
/// This method should only be called in generator functions.
void _checkForYieldOfInvalidType(
BodyInferenceContext bodyContext,
YieldStatement node, {
required bool isYieldEach,
}) {
var expression = node.expression;
var expressionType = expression.typeOrThrow;
DartType impliedReturnType;
if (isYieldEach) {
impliedReturnType = expressionType;
} else if (bodyContext.isSynchronous) {
impliedReturnType = _typeProvider.iterableType(expressionType);
} else {
impliedReturnType = _typeProvider.streamType(expressionType);
}
var imposedReturnType = bodyContext.imposedType;
if (imposedReturnType != null) {
if (isYieldEach) {
if (!_typeSystem.isAssignableTo(impliedReturnType, imposedReturnType,
strictCasts: _resolver.analysisOptions.strictCasts)) {
_errorReporter.atNode(
expression,
CompileTimeErrorCode.YIELD_EACH_OF_INVALID_TYPE,
arguments: [impliedReturnType, imposedReturnType],
);
return;
}
} else {
var imposedSequenceType = imposedReturnType.asInstanceOf(
bodyContext.isSynchronous
? _typeProvider.iterableElement
: _typeProvider.streamElement,
);
if (imposedSequenceType != null) {
var imposedValueType = imposedSequenceType.typeArguments[0];
if (!_typeSystem.isAssignableTo(expressionType, imposedValueType,
strictCasts: _resolver.analysisOptions.strictCasts)) {
_errorReporter.atNode(
expression,
CompileTimeErrorCode.YIELD_OF_INVALID_TYPE,
arguments: [expressionType, imposedValueType],
);
return;
}
}
}
}
if (isYieldEach) {
// Since the declared return type might have been "dynamic", we need to
// also check that the implied return type is assignable to generic
// Iterable/Stream.
DartType requiredReturnType;
if (bodyContext.isSynchronous) {
requiredReturnType = _typeProvider.iterableDynamicType;
} else {
requiredReturnType = _typeProvider.streamDynamicType;
}
if (!_typeSystem.isAssignableTo(impliedReturnType, requiredReturnType,
strictCasts: _resolver.analysisOptions.strictCasts)) {
_errorReporter.atNode(
expression,
CompileTimeErrorCode.YIELD_EACH_OF_INVALID_TYPE,
arguments: [impliedReturnType, requiredReturnType],
);
}
}
}
DartType _computeContextType(
BodyInferenceContext bodyContext,
YieldStatement node,
) {
var elementType = bodyContext.contextType;
if (elementType != null) {
var contextType = elementType;
if (node.star != null) {
contextType = bodyContext.isSynchronous
? _typeProvider.iterableType(elementType)
: _typeProvider.streamType(elementType);
}
return contextType;
} else {
return UnknownInferredType.instance;
}
}
void _resolve_generator(
BodyInferenceContext bodyContext,
YieldStatement node,
) {
_resolver.analyzeExpression(node.expression,
SharedTypeSchemaView(_computeContextType(bodyContext, node)));
_resolver.popRewrite();
if (node.star != null) {
_resolver.nullableDereferenceVerifier.expression(
CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_IN_YIELD_EACH,
node.expression,
);
}
bodyContext.addYield(node);
_checkForYieldOfInvalidType(bodyContext, node,
isYieldEach: node.star != null);
_checkForUseOfVoidResult(node.expression);
}
void _resolve_notGenerator(YieldStatement node) {
node.expression.accept(_resolver);
_errorReporter.atNode(
node,
node.star != null
? CompileTimeErrorCode.YIELD_EACH_IN_NON_GENERATOR
: CompileTimeErrorCode.YIELD_IN_NON_GENERATOR,
);
_checkForUseOfVoidResult(node.expression);
}
}