blob: 02e043b1b72bcc6de8cbcb53b218f9cef245f6a1 [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: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_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.inferenceContext.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.reportErrorForNode(
CompileTimeErrorCode.USE_OF_VOID_RESULT,
expression.methodName,
);
} else {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.USE_OF_VOID_RESULT,
expression,
);
}
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 &&
!_typeSystem.isAssignableTo(impliedReturnType, imposedReturnType)) {
if (isYieldEach) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.YIELD_EACH_OF_INVALID_TYPE,
expression,
[impliedReturnType, imposedReturnType],
);
return;
}
var imposedTypeAsInstanceOf = bodyContext.isSynchronous
? imposedReturnType.asInstanceOf(_typeProvider.iterableElement)
: imposedReturnType.asInstanceOf(_typeProvider.streamElement);
var imposedValueType = imposedTypeAsInstanceOf?.typeArguments[0];
if (imposedValueType != null) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.YIELD_OF_INVALID_TYPE,
expression,
[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)) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.YIELD_EACH_OF_INVALID_TYPE,
expression,
[impliedReturnType, requiredReturnType],
);
}
}
}
void _resolve_generator(
BodyInferenceContext bodyContext,
YieldStatement node,
) {
_setContextType(bodyContext, node);
node.expression.accept(_resolver);
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.reportErrorForNode(
node.star != null
? CompileTimeErrorCode.YIELD_EACH_IN_NON_GENERATOR
: CompileTimeErrorCode.YIELD_IN_NON_GENERATOR,
node,
);
_checkForUseOfVoidResult(node.expression);
}
void _setContextType(
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);
}
InferenceContext.setType(node.expression, contextType);
}
}
}