fix(43160): improve error location for functions without explicit return (#43367)

* fix(43160): improve error location for functions without explicit return

* handle functions returning never
This commit is contained in:
Zen 2021-04-06 22:21:02 +08:00 committed by GitHub
parent 3b06ef1a73
commit 3cf26e44ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 303 additions and 5 deletions

View File

@ -30574,18 +30574,18 @@ namespace ts {
}
const hasExplicitReturn = func.flags & NodeFlags.HasExplicitReturn;
const errorNode = getEffectiveReturnTypeNode(func) || func;
if (type && type.flags & TypeFlags.Never) {
error(getEffectiveReturnTypeNode(func), Diagnostics.A_function_returning_never_cannot_have_a_reachable_end_point);
error(errorNode, Diagnostics.A_function_returning_never_cannot_have_a_reachable_end_point);
}
else if (type && !hasExplicitReturn) {
// minimal check: function has syntactic return type annotation and no explicit return statements in the body
// 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(getEffectiveReturnTypeNode(func), Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value);
error(errorNode, Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value);
}
else if (type && strictNullChecks && !isTypeAssignableTo(undefinedType, type)) {
error(getEffectiveReturnTypeNode(func) || func, Diagnostics.Function_lacks_ending_return_statement_and_return_type_does_not_include_undefined);
error(errorNode, Diagnostics.Function_lacks_ending_return_statement_and_return_type_does_not_include_undefined);
}
else if (compilerOptions.noImplicitReturns) {
if (!type) {
@ -30600,7 +30600,7 @@ namespace ts {
return;
}
}
error(getEffectiveReturnTypeNode(func) || func, Diagnostics.Not_all_code_paths_return_a_value);
error(errorNode, Diagnostics.Not_all_code_paths_return_a_value);
}
}

View File

@ -0,0 +1,74 @@
tests/cases/conformance/jsdoc/foo.js(7,10): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
tests/cases/conformance/jsdoc/foo.js(13,5): error TS2322: Type 'string' is not assignable to type 'number'.
tests/cases/conformance/jsdoc/foo.js(16,60): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
tests/cases/conformance/jsdoc/foo.js(21,20): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
tests/cases/conformance/jsdoc/foo.js(31,10): error TS2534: A function returning 'never' cannot have a reachable end point.
tests/cases/conformance/jsdoc/foo.js(37,5): error TS2322: Type 'string' is not assignable to type 'never'.
tests/cases/conformance/jsdoc/foo.js(40,56): error TS2534: A function returning 'never' cannot have a reachable end point.
tests/cases/conformance/jsdoc/foo.js(45,18): error TS2534: A function returning 'never' cannot have a reachable end point.
==== tests/cases/conformance/jsdoc/foo.js (8 errors) ====
/**
* @callback FunctionReturningPromise
* @returns {Promise<number>}
*/
/** @type {FunctionReturningPromise} */
function testPromise1() {
~~~~~~~~~~~~
!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
console.log("Nope");
}
/** @type {FunctionReturningPromise} */
async function testPromise2() {
return "asd";
~~~~~~~~~~~~~
!!! error TS2322: Type 'string' is not assignable to type 'number'.
}
var testPromise3 = /** @type {FunctionReturningPromise} */ function() {
~~~~~~~~
!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
console.log("test")
}
/** @type {FunctionReturningPromise} */
var testPromise4 = function() {
~~~~~~~~
!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
console.log("test")
}
/**
* @callback FunctionReturningNever
* @returns {never}
*/
/** @type {FunctionReturningNever} */
function testNever1() {
~~~~~~~~~~
!!! error TS2534: A function returning 'never' cannot have a reachable end point.
}
/** @type {FunctionReturningNever} */
async function testNever2() {
return "asd";
~~~~~~~~~~~~~
!!! error TS2322: Type 'string' is not assignable to type 'never'.
}
var testNever3 = /** @type {FunctionReturningNever} */ function() {
~~~~~~~~
!!! error TS2534: A function returning 'never' cannot have a reachable end point.
console.log("test")
}
/** @type {FunctionReturningNever} */
var testNever4 = function() {
~~~~~~~~
!!! error TS2534: A function returning 'never' cannot have a reachable end point.
console.log("test")
}

View File

@ -0,0 +1,78 @@
=== tests/cases/conformance/jsdoc/foo.js ===
/**
* @callback FunctionReturningPromise
* @returns {Promise<number>}
*/
/** @type {FunctionReturningPromise} */
function testPromise1() {
>testPromise1 : Symbol(testPromise1, Decl(foo.js, 0, 0))
console.log("Nope");
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
}
/** @type {FunctionReturningPromise} */
async function testPromise2() {
>testPromise2 : Symbol(testPromise2, Decl(foo.js, 8, 1))
return "asd";
}
var testPromise3 = /** @type {FunctionReturningPromise} */ function() {
>testPromise3 : Symbol(testPromise3, Decl(foo.js, 15, 3))
console.log("test")
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
}
/** @type {FunctionReturningPromise} */
var testPromise4 = function() {
>testPromise4 : Symbol(testPromise4, Decl(foo.js, 20, 3))
console.log("test")
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
}
/**
* @callback FunctionReturningNever
* @returns {never}
*/
/** @type {FunctionReturningNever} */
function testNever1() {
>testNever1 : Symbol(testNever1, Decl(foo.js, 22, 1))
}
/** @type {FunctionReturningNever} */
async function testNever2() {
>testNever2 : Symbol(testNever2, Decl(foo.js, 32, 1))
return "asd";
}
var testNever3 = /** @type {FunctionReturningNever} */ function() {
>testNever3 : Symbol(testNever3, Decl(foo.js, 39, 3))
console.log("test")
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
}
/** @type {FunctionReturningNever} */
var testNever4 = function() {
>testNever4 : Symbol(testNever4, Decl(foo.js, 44, 3))
console.log("test")
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
}

View File

@ -0,0 +1,94 @@
=== tests/cases/conformance/jsdoc/foo.js ===
/**
* @callback FunctionReturningPromise
* @returns {Promise<number>}
*/
/** @type {FunctionReturningPromise} */
function testPromise1() {
>testPromise1 : () => Promise<number>
console.log("Nope");
>console.log("Nope") : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>"Nope" : "Nope"
}
/** @type {FunctionReturningPromise} */
async function testPromise2() {
>testPromise2 : () => Promise<number>
return "asd";
>"asd" : "asd"
}
var testPromise3 = /** @type {FunctionReturningPromise} */ function() {
>testPromise3 : FunctionReturningPromise
>function() { console.log("test")} : () => Promise<number>
console.log("test")
>console.log("test") : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>"test" : "test"
}
/** @type {FunctionReturningPromise} */
var testPromise4 = function() {
>testPromise4 : FunctionReturningPromise
>function() { console.log("test")} : () => Promise<number>
console.log("test")
>console.log("test") : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>"test" : "test"
}
/**
* @callback FunctionReturningNever
* @returns {never}
*/
/** @type {FunctionReturningNever} */
function testNever1() {
>testNever1 : () => never
}
/** @type {FunctionReturningNever} */
async function testNever2() {
>testNever2 : () => never
return "asd";
>"asd" : "asd"
}
var testNever3 = /** @type {FunctionReturningNever} */ function() {
>testNever3 : FunctionReturningNever
>function() { console.log("test")} : () => never
console.log("test")
>console.log("test") : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>"test" : "test"
}
/** @type {FunctionReturningNever} */
var testNever4 = function() {
>testNever4 : FunctionReturningNever
>function() { console.log("test")} : () => never
console.log("test")
>console.log("test") : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>"test" : "test"
}

View File

@ -0,0 +1,52 @@
// @noEmit: true
// @allowJs: true
// @checkJs: true
// @Filename: foo.js
/**
* @callback FunctionReturningPromise
* @returns {Promise<number>}
*/
/** @type {FunctionReturningPromise} */
function testPromise1() {
console.log("Nope");
}
/** @type {FunctionReturningPromise} */
async function testPromise2() {
return "asd";
}
var testPromise3 = /** @type {FunctionReturningPromise} */ function() {
console.log("test")
}
/** @type {FunctionReturningPromise} */
var testPromise4 = function() {
console.log("test")
}
/**
* @callback FunctionReturningNever
* @returns {never}
*/
/** @type {FunctionReturningNever} */
function testNever1() {
}
/** @type {FunctionReturningNever} */
async function testNever2() {
return "asd";
}
var testNever3 = /** @type {FunctionReturningNever} */ function() {
console.log("test")
}
/** @type {FunctionReturningNever} */
var testNever4 = function() {
console.log("test")
}