| // 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); |
| } |
| } |