| // 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/src/dart/ast/ast.dart'; | |
| import 'package:analyzer/src/dart/ast/extensions.dart'; | |
| import 'package:analyzer/src/dart/element/type.dart'; | |
| import 'package:analyzer/src/dart/element/type_provider.dart'; | |
| import 'package:analyzer/src/dart/element/type_system.dart'; | |
| import 'package:analyzer/src/dart/resolver/body_inference_context.dart'; | |
| import 'package:analyzer/src/dart/resolver/type_analyzer.dart'; | |
| import 'package:analyzer/src/diagnostic/diagnostic.dart' as diag; | |
| import 'package:analyzer/src/error/listener.dart'; | |
| /// Helper for resolving [YieldStatement]s. | |
| class YieldStatementResolver { | |
| final TypeAnalyzer _typeAnalyzer; | |
| YieldStatementResolver({required TypeAnalyzer typeAnalyzer}) | |
| : _typeAnalyzer = typeAnalyzer; | |
| DiagnosticReporter get _diagnosticReporter => | |
| _typeAnalyzer.diagnosticReporter; | |
| TypeProviderImpl get _typeProvider => _typeAnalyzer.typeProvider; | |
| TypeSystemImpl get _typeSystem => _typeAnalyzer.typeSystem; | |
| void resolve(YieldStatementImpl node) { | |
| var bodyContext = _typeAnalyzer.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 [diag.useOfVoidResult]. | |
| /// | |
| // TODO(scheglov): This is duplicate | |
| // TODO(scheglov): Also in [BoolExpressionVerifier] | |
| bool _checkForUseOfVoidResult(Expression expression) { | |
| if (expression.staticType is! VoidTypeImpl) { | |
| return false; | |
| } | |
| _diagnosticReporter.report(diag.useOfVoidResult.at(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.expression2; | |
| var expressionType = expression.typeOrThrow; | |
| TypeImpl impliedReturnType; | |
| if (isYieldEach) { | |
| impliedReturnType = expressionType; | |
| } else if (bodyContext.isSync) { | |
| impliedReturnType = _typeProvider.iterableType(expressionType); | |
| } else { | |
| impliedReturnType = _typeProvider.streamType(expressionType); | |
| } | |
| var imposedReturnType = bodyContext.imposedType; | |
| if (imposedReturnType != null) { | |
| imposedReturnType = _typeSystem.unionFreeType(imposedReturnType); | |
| if (isYieldEach) { | |
| if (!_typeSystem.isAssignableTo( | |
| impliedReturnType, | |
| imposedReturnType, | |
| strictCasts: _typeAnalyzer.analysisOptions.strictCasts, | |
| )) { | |
| _diagnosticReporter.report( | |
| diag.yieldEachOfInvalidType | |
| .withArguments( | |
| actualType: impliedReturnType, | |
| expectedType: imposedReturnType, | |
| ) | |
| .at(expression), | |
| ); | |
| return; | |
| } | |
| } else { | |
| var imposedSequenceType = imposedReturnType.asInstanceOf( | |
| bodyContext.isSync | |
| ? _typeProvider.iterableElement | |
| : _typeProvider.streamElement, | |
| ); | |
| if (imposedSequenceType != null) { | |
| var imposedValueType = imposedSequenceType.typeArguments[0]; | |
| if (!_typeSystem.isAssignableTo( | |
| expressionType, | |
| imposedValueType, | |
| strictCasts: _typeAnalyzer.analysisOptions.strictCasts, | |
| )) { | |
| _diagnosticReporter.report( | |
| diag.yieldOfInvalidType | |
| .withArguments( | |
| actualType: expressionType, | |
| expectedType: imposedValueType, | |
| ) | |
| .at(expression), | |
| ); | |
| 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. | |
| TypeImpl requiredReturnType; | |
| if (bodyContext.isSync) { | |
| requiredReturnType = _typeProvider.iterableDynamicType; | |
| } else { | |
| requiredReturnType = _typeProvider.streamDynamicType; | |
| } | |
| if (!_typeSystem.isAssignableTo( | |
| impliedReturnType, | |
| requiredReturnType, | |
| strictCasts: _typeAnalyzer.analysisOptions.strictCasts, | |
| )) { | |
| _diagnosticReporter.report( | |
| diag.yieldEachOfInvalidType | |
| .withArguments( | |
| actualType: impliedReturnType, | |
| expectedType: requiredReturnType, | |
| ) | |
| .at(expression), | |
| ); | |
| } | |
| } | |
| } | |
| void _resolve_generator( | |
| BodyInferenceContext bodyContext, | |
| YieldStatementImpl node, | |
| ) { | |
| _typeAnalyzer.analyzeYieldStatement( | |
| node, | |
| node.expression2, | |
| isYieldStar: node.star != null, | |
| suspensionOffset: node.semicolon.offset, | |
| ); | |
| _typeAnalyzer.popRewrite(); | |
| if (node.star != null) { | |
| _typeAnalyzer.nullableDereferenceVerifier.expression( | |
| diag.uncheckedUseOfNullableValueInYieldEach, | |
| node.expression2, | |
| ); | |
| } | |
| bodyContext.addYield(node); | |
| _checkForYieldOfInvalidType( | |
| bodyContext, | |
| node, | |
| isYieldEach: node.star != null, | |
| ); | |
| _checkForUseOfVoidResult(node.expression2); | |
| } | |
| void _resolve_notGenerator(YieldStatementImpl node) { | |
| _typeAnalyzer.analyzeExpression( | |
| node.expression2, | |
| _typeAnalyzer.operations.unknownType, | |
| ); | |
| _typeAnalyzer.popRewrite(); | |
| _diagnosticReporter.report( | |
| (node.star != null | |
| ? diag.yieldEachInNonGenerator | |
| : diag.yieldInNonGenerator) | |
| .at(node), | |
| ); | |
| _checkForUseOfVoidResult(node.expression2); | |
| } | |
| } |