From d53af092c70a343e33846ac54abdf42c44d0793b Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 12 Dec 2017 07:50:11 -0800 Subject: [PATCH] Always check a return expression in a generator (#20621) --- src/compiler/checker.ts | 81 ++++++++++--------- ...eratorReturnExpressionIsChecked.errors.txt | 10 +++ .../generatorReturnExpressionIsChecked.js | 10 +++ ...generatorReturnExpressionIsChecked.symbols | 8 ++ .../generatorReturnExpressionIsChecked.types | 9 +++ .../generatorReturnExpressionIsChecked.ts | 5 ++ 6 files changed, 83 insertions(+), 40 deletions(-) create mode 100644 tests/baselines/reference/generatorReturnExpressionIsChecked.errors.txt create mode 100644 tests/baselines/reference/generatorReturnExpressionIsChecked.js create mode 100644 tests/baselines/reference/generatorReturnExpressionIsChecked.symbols create mode 100644 tests/baselines/reference/generatorReturnExpressionIsChecked.types create mode 100644 tests/cases/compiler/generatorReturnExpressionIsChecked.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1b193ad3c2e..95c7d33d5aa 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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); } } diff --git a/tests/baselines/reference/generatorReturnExpressionIsChecked.errors.txt b/tests/baselines/reference/generatorReturnExpressionIsChecked.errors.txt new file mode 100644 index 00000000000..a1fc28ddb45 --- /dev/null +++ b/tests/baselines/reference/generatorReturnExpressionIsChecked.errors.txt @@ -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 { + return invalid; + ~~~~~~~ +!!! error TS2304: Cannot find name 'invalid'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/generatorReturnExpressionIsChecked.js b/tests/baselines/reference/generatorReturnExpressionIsChecked.js new file mode 100644 index 00000000000..58eb6f90442 --- /dev/null +++ b/tests/baselines/reference/generatorReturnExpressionIsChecked.js @@ -0,0 +1,10 @@ +//// [generatorReturnExpressionIsChecked.ts] +function* f(): Iterator { + return invalid; +} + + +//// [generatorReturnExpressionIsChecked.js] +function* f() { + return invalid; +} diff --git a/tests/baselines/reference/generatorReturnExpressionIsChecked.symbols b/tests/baselines/reference/generatorReturnExpressionIsChecked.symbols new file mode 100644 index 00000000000..cd3fc96637c --- /dev/null +++ b/tests/baselines/reference/generatorReturnExpressionIsChecked.symbols @@ -0,0 +1,8 @@ +=== tests/cases/compiler/generatorReturnExpressionIsChecked.ts === +function* f(): Iterator { +>f : Symbol(f, Decl(generatorReturnExpressionIsChecked.ts, 0, 0)) +>Iterator : Symbol(Iterator, Decl(lib.es2015.iterable.d.ts, --, --)) + + return invalid; +} + diff --git a/tests/baselines/reference/generatorReturnExpressionIsChecked.types b/tests/baselines/reference/generatorReturnExpressionIsChecked.types new file mode 100644 index 00000000000..13bb5f83a94 --- /dev/null +++ b/tests/baselines/reference/generatorReturnExpressionIsChecked.types @@ -0,0 +1,9 @@ +=== tests/cases/compiler/generatorReturnExpressionIsChecked.ts === +function* f(): Iterator { +>f : () => Iterator +>Iterator : Iterator + + return invalid; +>invalid : any +} + diff --git a/tests/cases/compiler/generatorReturnExpressionIsChecked.ts b/tests/cases/compiler/generatorReturnExpressionIsChecked.ts new file mode 100644 index 00000000000..06153163868 --- /dev/null +++ b/tests/cases/compiler/generatorReturnExpressionIsChecked.ts @@ -0,0 +1,5 @@ +// @target: esnext + +function* f(): Iterator { + return invalid; +}