Merge pull request #5733 from Microsoft/unconditionalNoImplicitReturns

apply 'noImplicitReturns' rule for functions that don't have type an…
This commit is contained in:
Vladimir Matveev 2015-11-25 12:25:51 -08:00
commit 937ce71a12
7 changed files with 644 additions and 27 deletions

View File

@ -9854,17 +9854,25 @@ namespace ts {
return aggregatedTypes;
}
// TypeScript Specification 1.0 (6.3) - July 2014
// An explicitly typed function whose return type isn't the Void or the Any type
// must have at least one return statement somewhere in its body.
// An exception to this rule is if the function implementation consists of a single 'throw' statement.
/*
*TypeScript Specification 1.0 (6.3) - July 2014
* An explicitly typed function whose return type isn't the Void or the Any type
* must have at least one return statement somewhere in its body.
* An exception to this rule is if the function implementation consists of a single 'throw' statement.
* @param returnType - return type of the function, can be undefined if return type is not explicitly specified
*/
function checkAllCodePathsInNonVoidFunctionReturnOrThrow(func: FunctionLikeDeclaration, returnType: Type): void {
if (!produceDiagnostics) {
return;
}
// Functions that return 'void' or 'any' don't need any return expressions.
if (returnType === voidType || isTypeAny(returnType)) {
// Functions with with an explicitly specified 'void' or 'any' return type don't need any return expressions.
if (returnType && (returnType === voidType || isTypeAny(returnType))) {
return;
}
// if return type is not specified then we'll do the check only if 'noImplicitReturns' option is set
if (!returnType && !compilerOptions.noImplicitReturns) {
return;
}
@ -9874,13 +9882,14 @@ namespace ts {
return;
}
if (func.flags & NodeFlags.HasExplicitReturn) {
if (!returnType || func.flags & NodeFlags.HasExplicitReturn) {
if (compilerOptions.noImplicitReturns) {
error(func.type, Diagnostics.Not_all_code_paths_return_a_value);
error(func.type || func, Diagnostics.Not_all_code_paths_return_a_value);
}
}
else {
// This function does not conform to the specification.
// NOTE: having returnType !== undefined is a precondition for entering this branch so func.type will always be present
error(func.type, Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value);
}
}
@ -9955,14 +9964,10 @@ namespace ts {
emitAwaiter = true;
}
const returnType = node.type && getTypeFromTypeNode(node.type);
let promisedType: Type;
if (returnType && isAsync) {
promisedType = checkAsyncFunctionReturnType(node);
}
if (returnType && !node.asteriskToken) {
checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, isAsync ? promisedType : returnType);
const returnOrPromisedType = node.type && (isAsync ? checkAsyncFunctionReturnType(node) : getTypeFromTypeNode(node.type));
if (!node.asteriskToken) {
// return is not necessary in the body of generators
checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnOrPromisedType);
}
if (node.body) {
@ -9985,13 +9990,13 @@ namespace ts {
// check assignability of the awaited type of the expression body against the promised type of
// its return type annotation.
const exprType = checkExpression(<Expression>node.body);
if (returnType) {
if (returnOrPromisedType) {
if (isAsync) {
const awaitedType = checkAwaitedType(exprType, node.body, Diagnostics.Expression_body_for_async_arrow_function_does_not_have_a_valid_callable_then_member);
checkTypeAssignableTo(awaitedType, promisedType, node.body);
checkTypeAssignableTo(awaitedType, returnOrPromisedType, node.body);
}
else {
checkTypeAssignableTo(exprType, returnType, node.body);
checkTypeAssignableTo(exprType, returnOrPromisedType, node.body);
}
}
@ -12131,14 +12136,9 @@ namespace ts {
}
checkSourceElement(node.body);
if (node.type && !isAccessor(node.kind) && !node.asteriskToken) {
const returnType = getTypeFromTypeNode(node.type);
let promisedType: Type;
if (isAsync) {
promisedType = checkAsyncFunctionReturnType(node);
}
checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, isAsync ? promisedType : returnType);
if (!isAccessor(node.kind) && !node.asteriskToken) {
const returnOrPromisedType = node.type && (isAsync ? checkAsyncFunctionReturnType(node) : getTypeFromTypeNode(node.type));
checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnOrPromisedType);
}
if (produceDiagnostics && !node.type) {

View File

@ -0,0 +1,162 @@
tests/cases/compiler/reachabilityChecks6.ts(6,10): error TS7030: Not all code paths return a value.
tests/cases/compiler/reachabilityChecks6.ts(19,10): error TS7030: Not all code paths return a value.
tests/cases/compiler/reachabilityChecks6.ts(31,10): error TS7030: Not all code paths return a value.
tests/cases/compiler/reachabilityChecks6.ts(41,10): error TS7030: Not all code paths return a value.
tests/cases/compiler/reachabilityChecks6.ts(52,10): error TS7030: Not all code paths return a value.
tests/cases/compiler/reachabilityChecks6.ts(80,10): error TS7030: Not all code paths return a value.
tests/cases/compiler/reachabilityChecks6.ts(86,13): error TS7027: Unreachable code detected.
tests/cases/compiler/reachabilityChecks6.ts(94,10): error TS7030: Not all code paths return a value.
tests/cases/compiler/reachabilityChecks6.ts(116,10): error TS7030: Not all code paths return a value.
tests/cases/compiler/reachabilityChecks6.ts(123,13): error TS7027: Unreachable code detected.
==== tests/cases/compiler/reachabilityChecks6.ts (10 errors) ====
function f0(x) {
while (true);
}
function f1(x) {
~~
!!! error TS7030: Not all code paths return a value.
if (x) {
return 1
}
}
function f2(x) {
while (x) {
throw new Error();
}
return 1;
}
function f3(x) {
~~
!!! error TS7030: Not all code paths return a value.
while (x) {
throw new Error();
}
}
function f3_1 (x) {
while (x) {
}
throw new Error();
}
function f4(x) {
~~
!!! error TS7030: Not all code paths return a value.
try {
if (x) {
return 1;
}
}
catch (e) {
}
}
function f5(x) {
~~
!!! error TS7030: Not all code paths return a value.
try {
if (x) {
return 1;
}
}
catch (e) {
return 2;
}
}
function f6(x) {
~~
!!! error TS7030: Not all code paths return a value.
try {
if (x) {
return 1;
}
else
{
throw new Error();
}
}
catch (e) {
}
}
function f7(x) {
try {
if (x) {
return 1;
}
else {
throw new Error();
}
}
catch (e) {
return 1;
}
}
function f8(x) {
~~
!!! error TS7030: Not all code paths return a value.
try {
if (true) {
x++;
}
else {
return 1;
~~~~~~
!!! error TS7027: Unreachable code detected.
}
}
catch (e) {
return 1;
}
}
function f9(x) {
~~
!!! error TS7030: Not all code paths return a value.
try {
while (false) {
return 1;
}
}
catch (e) {
return 1;
}
}
function f10(x) {
try {
do {
x++;
} while (true);
}
catch (e) {
return 1;
}
}
function f11(x) {
~~~
!!! error TS7030: Not all code paths return a value.
test:
try {
do {
do {
break test;
} while (true);
x++;
~
!!! error TS7027: Unreachable code detected.
} while (true);
}
catch (e) {
return 1;
}
}

View File

@ -0,0 +1,247 @@
//// [reachabilityChecks6.ts]
function f0(x) {
while (true);
}
function f1(x) {
if (x) {
return 1
}
}
function f2(x) {
while (x) {
throw new Error();
}
return 1;
}
function f3(x) {
while (x) {
throw new Error();
}
}
function f3_1 (x) {
while (x) {
}
throw new Error();
}
function f4(x) {
try {
if (x) {
return 1;
}
}
catch (e) {
}
}
function f5(x) {
try {
if (x) {
return 1;
}
}
catch (e) {
return 2;
}
}
function f6(x) {
try {
if (x) {
return 1;
}
else
{
throw new Error();
}
}
catch (e) {
}
}
function f7(x) {
try {
if (x) {
return 1;
}
else {
throw new Error();
}
}
catch (e) {
return 1;
}
}
function f8(x) {
try {
if (true) {
x++;
}
else {
return 1;
}
}
catch (e) {
return 1;
}
}
function f9(x) {
try {
while (false) {
return 1;
}
}
catch (e) {
return 1;
}
}
function f10(x) {
try {
do {
x++;
} while (true);
}
catch (e) {
return 1;
}
}
function f11(x) {
test:
try {
do {
do {
break test;
} while (true);
x++;
} while (true);
}
catch (e) {
return 1;
}
}
//// [reachabilityChecks6.js]
function f0(x) {
while (true)
;
}
function f1(x) {
if (x) {
return 1;
}
}
function f2(x) {
while (x) {
throw new Error();
}
return 1;
}
function f3(x) {
while (x) {
throw new Error();
}
}
function f3_1(x) {
while (x) {
}
throw new Error();
}
function f4(x) {
try {
if (x) {
return 1;
}
}
catch (e) {
}
}
function f5(x) {
try {
if (x) {
return 1;
}
}
catch (e) {
return 2;
}
}
function f6(x) {
try {
if (x) {
return 1;
}
else {
throw new Error();
}
}
catch (e) {
}
}
function f7(x) {
try {
if (x) {
return 1;
}
else {
throw new Error();
}
}
catch (e) {
return 1;
}
}
function f8(x) {
try {
if (true) {
x++;
}
else {
return 1;
}
}
catch (e) {
return 1;
}
}
function f9(x) {
try {
while (false) {
return 1;
}
}
catch (e) {
return 1;
}
}
function f10(x) {
try {
do {
x++;
} while (true);
}
catch (e) {
return 1;
}
}
function f11(x) {
test: try {
do {
do {
break test;
} while (true);
x++;
} while (true);
}
catch (e) {
return 1;
}
}

View File

@ -0,0 +1,21 @@
tests/cases/compiler/reachabilityChecks7.ts(3,16): error TS7030: Not all code paths return a value.
tests/cases/compiler/reachabilityChecks7.ts(6,9): error TS7030: Not all code paths return a value.
==== tests/cases/compiler/reachabilityChecks7.ts (2 errors) ====
// async function without return type annotation - error
async function f1() {
~~
!!! error TS7030: Not all code paths return a value.
}
let x = async function() {
~~~~~
!!! error TS7030: Not all code paths return a value.
}
// async function with which promised type is void - return can be omitted
async function f2(): Promise<void> {
}

View File

@ -0,0 +1,42 @@
//// [reachabilityChecks7.ts]
// async function without return type annotation - error
async function f1() {
}
let x = async function() {
}
// async function with which promised type is void - return can be omitted
async function f2(): Promise<void> {
}
//// [reachabilityChecks7.js]
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promise, generator) {
return new Promise(function (resolve, reject) {
generator = generator.call(thisArg, _arguments);
function cast(value) { return value instanceof Promise && value.constructor === Promise ? value : new Promise(function (resolve) { resolve(value); }); }
function onfulfill(value) { try { step("next", value); } catch (e) { reject(e); } }
function onreject(value) { try { step("throw", value); } catch (e) { reject(e); } }
function step(verb, value) {
var result = generator[verb](value);
result.done ? resolve(result.value) : cast(result.value).then(onfulfill, onreject);
}
step("next", void 0);
});
};
// async function without return type annotation - error
function f1() {
return __awaiter(this, void 0, Promise, function* () {
});
}
let x = function () {
return __awaiter(this, void 0, Promise, function* () {
});
};
// async function with which promised type is void - return can be omitted
function f2() {
return __awaiter(this, void 0, Promise, function* () {
});
}

View File

@ -0,0 +1,131 @@
// @allowUnreachableCode: false
// @noImplicitReturns: true
function f0(x) {
while (true);
}
function f1(x) {
if (x) {
return 1
}
}
function f2(x) {
while (x) {
throw new Error();
}
return 1;
}
function f3(x) {
while (x) {
throw new Error();
}
}
function f3_1 (x) {
while (x) {
}
throw new Error();
}
function f4(x) {
try {
if (x) {
return 1;
}
}
catch (e) {
}
}
function f5(x) {
try {
if (x) {
return 1;
}
}
catch (e) {
return 2;
}
}
function f6(x) {
try {
if (x) {
return 1;
}
else
{
throw new Error();
}
}
catch (e) {
}
}
function f7(x) {
try {
if (x) {
return 1;
}
else {
throw new Error();
}
}
catch (e) {
return 1;
}
}
function f8(x) {
try {
if (true) {
x++;
}
else {
return 1;
}
}
catch (e) {
return 1;
}
}
function f9(x) {
try {
while (false) {
return 1;
}
}
catch (e) {
return 1;
}
}
function f10(x) {
try {
do {
x++;
} while (true);
}
catch (e) {
return 1;
}
}
function f11(x) {
test:
try {
do {
do {
break test;
} while (true);
x++;
} while (true);
}
catch (e) {
return 1;
}
}

View File

@ -0,0 +1,14 @@
// @target: ES6
// @noImplicitReturns: true
// async function without return type annotation - error
async function f1() {
}
let x = async function() {
}
// async function with which promised type is void - return can be omitted
async function f2(): Promise<void> {
}