Always check a return expression in a generator (#20621)

This commit is contained in:
Andy
2017-12-12 07:50:11 -08:00
committed by GitHub
parent 3aa192a870
commit d53af092c7
6 changed files with 83 additions and 40 deletions

View File

@@ -22199,57 +22199,58 @@ namespace ts {
function checkReturnStatement(node: ReturnStatement) {
// Grammar checking
if (!checkGrammarStatementInAmbientContext(node)) {
const functionBlock = getContainingFunction(node);
if (!functionBlock) {
grammarErrorOnFirstToken(node, Diagnostics.A_return_statement_can_only_be_used_within_a_function_body);
}
if (checkGrammarStatementInAmbientContext(node)) {
return;
}
const func = getContainingFunction(node);
if (func) {
const signature = getSignatureFromDeclaration(func);
const returnType = getReturnTypeOfSignature(signature);
const functionFlags = getFunctionFlags(func);
if (functionFlags & FunctionFlags.Generator) { // AsyncGenerator function or Generator function
if (!func) {
grammarErrorOnFirstToken(node, Diagnostics.A_return_statement_can_only_be_used_within_a_function_body);
return;
}
const signature = getSignatureFromDeclaration(func);
const returnType = getReturnTypeOfSignature(signature);
const functionFlags = getFunctionFlags(func);
const isGenerator = functionFlags & FunctionFlags.Generator;
if (strictNullChecks || node.expression || returnType.flags & TypeFlags.Never) {
const exprType = node.expression ? checkExpressionCached(node.expression) : undefinedType;
if (isGenerator) { // AsyncGenerator function or Generator function
// A generator does not need its return expressions checked against its return type.
// Instead, the yield expressions are checked against the element type.
// TODO: Check return expressions of generators when return type tracking is added
// TODO: Check return types of generators when return type tracking is added
// for generators.
return;
}
if (strictNullChecks || node.expression || returnType.flags & TypeFlags.Never) {
const exprType = node.expression ? checkExpressionCached(node.expression) : undefinedType;
if (func.kind === SyntaxKind.SetAccessor) {
if (node.expression) {
error(node, Diagnostics.Setters_cannot_return_a_value);
}
}
else if (func.kind === SyntaxKind.Constructor) {
if (node.expression && !checkTypeAssignableTo(exprType, returnType, node)) {
error(node, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class);
}
}
else if (getEffectiveReturnTypeNode(func) || isGetAccessorWithAnnotatedSetAccessor(func)) {
if (functionFlags & FunctionFlags.Async) { // Async function
const promisedType = getPromisedTypeOfPromise(returnType);
const awaitedType = checkAwaitedType(exprType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
if (promisedType) {
// If the function has a return type, but promisedType is
// undefined, an error will be reported in checkAsyncFunctionReturnType
// so we don't need to report one here.
checkTypeAssignableTo(awaitedType, promisedType, node);
}
}
else {
checkTypeAssignableTo(exprType, returnType, node);
}
else if (func.kind === SyntaxKind.SetAccessor) {
if (node.expression) {
error(node, Diagnostics.Setters_cannot_return_a_value);
}
}
else if (func.kind !== SyntaxKind.Constructor && compilerOptions.noImplicitReturns && !isUnwrappedReturnTypeVoidOrAny(func, returnType)) {
// The function has a return type, but the return statement doesn't have an expression.
error(node, Diagnostics.Not_all_code_paths_return_a_value);
else if (func.kind === SyntaxKind.Constructor) {
if (node.expression && !checkTypeAssignableTo(exprType, returnType, node)) {
error(node, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class);
}
}
else if (getEffectiveReturnTypeNode(func) || isGetAccessorWithAnnotatedSetAccessor(func)) {
if (functionFlags & FunctionFlags.Async) { // Async function
const promisedType = getPromisedTypeOfPromise(returnType);
const awaitedType = checkAwaitedType(exprType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
if (promisedType) {
// If the function has a return type, but promisedType is
// undefined, an error will be reported in checkAsyncFunctionReturnType
// so we don't need to report one here.
checkTypeAssignableTo(awaitedType, promisedType, node);
}
}
else {
checkTypeAssignableTo(exprType, returnType, node);
}
}
}
else if (func.kind !== SyntaxKind.Constructor && compilerOptions.noImplicitReturns && !isUnwrappedReturnTypeVoidOrAny(func, returnType) && !isGenerator) {
// The function has a return type, but the return statement doesn't have an expression.
error(node, Diagnostics.Not_all_code_paths_return_a_value);
}
}

View File

@@ -0,0 +1,10 @@
tests/cases/compiler/generatorReturnExpressionIsChecked.ts(2,12): error TS2304: Cannot find name 'invalid'.
==== tests/cases/compiler/generatorReturnExpressionIsChecked.ts (1 errors) ====
function* f(): Iterator<number> {
return invalid;
~~~~~~~
!!! error TS2304: Cannot find name 'invalid'.
}

View File

@@ -0,0 +1,10 @@
//// [generatorReturnExpressionIsChecked.ts]
function* f(): Iterator<number> {
return invalid;
}
//// [generatorReturnExpressionIsChecked.js]
function* f() {
return invalid;
}

View File

@@ -0,0 +1,8 @@
=== tests/cases/compiler/generatorReturnExpressionIsChecked.ts ===
function* f(): Iterator<number> {
>f : Symbol(f, Decl(generatorReturnExpressionIsChecked.ts, 0, 0))
>Iterator : Symbol(Iterator, Decl(lib.es2015.iterable.d.ts, --, --))
return invalid;
}

View File

@@ -0,0 +1,9 @@
=== tests/cases/compiler/generatorReturnExpressionIsChecked.ts ===
function* f(): Iterator<number> {
>f : () => Iterator<number>
>Iterator : Iterator<T>
return invalid;
>invalid : any
}

View File

@@ -0,0 +1,5 @@
// @target: esnext
function* f(): Iterator<number> {
return invalid;
}