From 0d970e4d8ef846cb614fccf9923830f51801c2a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 7 Oct 2023 19:40:44 +0200 Subject: [PATCH 1/3] Ignore awaited self tail calls when collecting the return type of an async function --- src/compiler/checker.ts | 7 +- .../simpleRecursionWithBaseCase.symbols | 67 -------- ...> simpleRecursionWithBaseCase1.errors.txt} | 16 +- ...ase.js => simpleRecursionWithBaseCase1.js} | 6 +- .../simpleRecursionWithBaseCase1.symbols | 67 ++++++++ ...pes => simpleRecursionWithBaseCase1.types} | 4 +- .../simpleRecursionWithBaseCase2.errors.txt | 69 ++++++++ .../simpleRecursionWithBaseCase2.symbols | 119 +++++++++++++ .../simpleRecursionWithBaseCase2.types | 156 ++++++++++++++++++ ...ase.ts => simpleRecursionWithBaseCase1.ts} | 0 .../compiler/simpleRecursionWithBaseCase2.ts | 61 +++++++ 11 files changed, 491 insertions(+), 81 deletions(-) delete mode 100644 tests/baselines/reference/simpleRecursionWithBaseCase.symbols rename tests/baselines/reference/{simpleRecursionWithBaseCase.errors.txt => simpleRecursionWithBaseCase1.errors.txt} (59%) rename tests/baselines/reference/{simpleRecursionWithBaseCase.js => simpleRecursionWithBaseCase1.js} (84%) create mode 100644 tests/baselines/reference/simpleRecursionWithBaseCase1.symbols rename tests/baselines/reference/{simpleRecursionWithBaseCase.types => simpleRecursionWithBaseCase1.types} (87%) create mode 100644 tests/baselines/reference/simpleRecursionWithBaseCase2.errors.txt create mode 100644 tests/baselines/reference/simpleRecursionWithBaseCase2.symbols create mode 100644 tests/baselines/reference/simpleRecursionWithBaseCase2.types rename tests/cases/compiler/{simpleRecursionWithBaseCase.ts => simpleRecursionWithBaseCase1.ts} (100%) create mode 100644 tests/cases/compiler/simpleRecursionWithBaseCase2.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2c9344679abd8..374e3b1f3d1b4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -36674,9 +36674,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { let hasReturnWithNoExpression = functionHasImplicitReturn(func); let hasReturnOfTypeNever = false; forEachReturnStatement(func.body as Block, returnStatement => { - const expr = returnStatement.expression; + let expr = returnStatement.expression; if (expr) { + expr = skipParentheses(expr); // Bare calls to this same function don't contribute to inference + // and `return await` is also safe to unwrap here + if (functionFlags & FunctionFlags.Async && expr.kind === SyntaxKind.AwaitExpression) { + expr = skipParentheses((expr as AwaitExpression).expression); + } if ( expr.kind === SyntaxKind.CallExpression && (expr as CallExpression).expression.kind === SyntaxKind.Identifier && diff --git a/tests/baselines/reference/simpleRecursionWithBaseCase.symbols b/tests/baselines/reference/simpleRecursionWithBaseCase.symbols deleted file mode 100644 index ffee8b1acba5d..0000000000000 --- a/tests/baselines/reference/simpleRecursionWithBaseCase.symbols +++ /dev/null @@ -1,67 +0,0 @@ -//// [tests/cases/compiler/simpleRecursionWithBaseCase.ts] //// - -=== simpleRecursionWithBaseCase.ts === -function fn1(n: number) { ->fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase.ts, 0, 0)) ->n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 0, 13)) - - if (n === 0) { ->n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 0, 13)) - - return 3; - } else { - return fn1(n - 1); ->fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase.ts, 0, 0)) ->n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 0, 13)) - } -} -const num: number = fn1(); ->num : Symbol(num, Decl(simpleRecursionWithBaseCase.ts, 7, 5)) ->fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase.ts, 0, 0)) - -function fn2(n: number) { ->fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase.ts, 7, 26)) ->n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 9, 13)) - - return fn2(n); ->fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase.ts, 7, 26)) ->n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 9, 13)) -} -const nev: never = fn2(); ->nev : Symbol(nev, Decl(simpleRecursionWithBaseCase.ts, 12, 5)) ->fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase.ts, 7, 26)) - -function fn3(n: number) { ->fn3 : Symbol(fn3, Decl(simpleRecursionWithBaseCase.ts, 12, 25)) ->n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 14, 13)) - - if (n === 0) { ->n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 14, 13)) - - return 3; - } else { - return fn1("hello world"); ->fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase.ts, 0, 0)) - } -} - -function fn4(n: number) { ->fn4 : Symbol(fn4, Decl(simpleRecursionWithBaseCase.ts, 20, 1)) ->n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 22, 13)) - - if (n === 0) { ->n : Symbol(n, Decl(simpleRecursionWithBaseCase.ts, 22, 13)) - - return 3; - } else { - return notfoundsymbol("hello world"); - } -} - -function fn5() { ->fn5 : Symbol(fn5, Decl(simpleRecursionWithBaseCase.ts, 28, 1)) - - return [fn5][0](); ->fn5 : Symbol(fn5, Decl(simpleRecursionWithBaseCase.ts, 28, 1)) -} - diff --git a/tests/baselines/reference/simpleRecursionWithBaseCase.errors.txt b/tests/baselines/reference/simpleRecursionWithBaseCase1.errors.txt similarity index 59% rename from tests/baselines/reference/simpleRecursionWithBaseCase.errors.txt rename to tests/baselines/reference/simpleRecursionWithBaseCase1.errors.txt index 5e30f2578cccb..77c1830dc6961 100644 --- a/tests/baselines/reference/simpleRecursionWithBaseCase.errors.txt +++ b/tests/baselines/reference/simpleRecursionWithBaseCase1.errors.txt @@ -1,11 +1,11 @@ -simpleRecursionWithBaseCase.ts(8,21): error TS2554: Expected 1 arguments, but got 0. -simpleRecursionWithBaseCase.ts(13,20): error TS2554: Expected 1 arguments, but got 0. -simpleRecursionWithBaseCase.ts(19,20): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. -simpleRecursionWithBaseCase.ts(27,16): error TS2304: Cannot find name 'notfoundsymbol'. -simpleRecursionWithBaseCase.ts(31,10): error TS7023: 'fn5' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. +simpleRecursionWithBaseCase1.ts(8,21): error TS2554: Expected 1 arguments, but got 0. +simpleRecursionWithBaseCase1.ts(13,20): error TS2554: Expected 1 arguments, but got 0. +simpleRecursionWithBaseCase1.ts(19,20): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. +simpleRecursionWithBaseCase1.ts(27,16): error TS2304: Cannot find name 'notfoundsymbol'. +simpleRecursionWithBaseCase1.ts(31,10): error TS7023: 'fn5' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. -==== simpleRecursionWithBaseCase.ts (5 errors) ==== +==== simpleRecursionWithBaseCase1.ts (5 errors) ==== function fn1(n: number) { if (n === 0) { return 3; @@ -16,7 +16,7 @@ simpleRecursionWithBaseCase.ts(31,10): error TS7023: 'fn5' implicitly has return const num: number = fn1(); ~~~~~ !!! error TS2554: Expected 1 arguments, but got 0. -!!! related TS6210 simpleRecursionWithBaseCase.ts:1:14: An argument for 'n' was not provided. +!!! related TS6210 simpleRecursionWithBaseCase1.ts:1:14: An argument for 'n' was not provided. function fn2(n: number) { return fn2(n); @@ -24,7 +24,7 @@ simpleRecursionWithBaseCase.ts(31,10): error TS7023: 'fn5' implicitly has return const nev: never = fn2(); ~~~~~ !!! error TS2554: Expected 1 arguments, but got 0. -!!! related TS6210 simpleRecursionWithBaseCase.ts:10:14: An argument for 'n' was not provided. +!!! related TS6210 simpleRecursionWithBaseCase1.ts:10:14: An argument for 'n' was not provided. function fn3(n: number) { if (n === 0) { diff --git a/tests/baselines/reference/simpleRecursionWithBaseCase.js b/tests/baselines/reference/simpleRecursionWithBaseCase1.js similarity index 84% rename from tests/baselines/reference/simpleRecursionWithBaseCase.js rename to tests/baselines/reference/simpleRecursionWithBaseCase1.js index 41711a8089af4..d743dffeaa636 100644 --- a/tests/baselines/reference/simpleRecursionWithBaseCase.js +++ b/tests/baselines/reference/simpleRecursionWithBaseCase1.js @@ -1,6 +1,6 @@ -//// [tests/cases/compiler/simpleRecursionWithBaseCase.ts] //// +//// [tests/cases/compiler/simpleRecursionWithBaseCase1.ts] //// -//// [simpleRecursionWithBaseCase.ts] +//// [simpleRecursionWithBaseCase1.ts] function fn1(n: number) { if (n === 0) { return 3; @@ -36,7 +36,7 @@ function fn5() { } -//// [simpleRecursionWithBaseCase.js] +//// [simpleRecursionWithBaseCase1.js] "use strict"; function fn1(n) { if (n === 0) { diff --git a/tests/baselines/reference/simpleRecursionWithBaseCase1.symbols b/tests/baselines/reference/simpleRecursionWithBaseCase1.symbols new file mode 100644 index 0000000000000..23d282c63ee0c --- /dev/null +++ b/tests/baselines/reference/simpleRecursionWithBaseCase1.symbols @@ -0,0 +1,67 @@ +//// [tests/cases/compiler/simpleRecursionWithBaseCase1.ts] //// + +=== simpleRecursionWithBaseCase1.ts === +function fn1(n: number) { +>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase1.ts, 0, 0)) +>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 0, 13)) + + if (n === 0) { +>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 0, 13)) + + return 3; + } else { + return fn1(n - 1); +>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase1.ts, 0, 0)) +>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 0, 13)) + } +} +const num: number = fn1(); +>num : Symbol(num, Decl(simpleRecursionWithBaseCase1.ts, 7, 5)) +>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase1.ts, 0, 0)) + +function fn2(n: number) { +>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase1.ts, 7, 26)) +>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 9, 13)) + + return fn2(n); +>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase1.ts, 7, 26)) +>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 9, 13)) +} +const nev: never = fn2(); +>nev : Symbol(nev, Decl(simpleRecursionWithBaseCase1.ts, 12, 5)) +>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase1.ts, 7, 26)) + +function fn3(n: number) { +>fn3 : Symbol(fn3, Decl(simpleRecursionWithBaseCase1.ts, 12, 25)) +>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 14, 13)) + + if (n === 0) { +>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 14, 13)) + + return 3; + } else { + return fn1("hello world"); +>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase1.ts, 0, 0)) + } +} + +function fn4(n: number) { +>fn4 : Symbol(fn4, Decl(simpleRecursionWithBaseCase1.ts, 20, 1)) +>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 22, 13)) + + if (n === 0) { +>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 22, 13)) + + return 3; + } else { + return notfoundsymbol("hello world"); + } +} + +function fn5() { +>fn5 : Symbol(fn5, Decl(simpleRecursionWithBaseCase1.ts, 28, 1)) + + return [fn5][0](); +>fn5 : Symbol(fn5, Decl(simpleRecursionWithBaseCase1.ts, 28, 1)) +} + diff --git a/tests/baselines/reference/simpleRecursionWithBaseCase.types b/tests/baselines/reference/simpleRecursionWithBaseCase1.types similarity index 87% rename from tests/baselines/reference/simpleRecursionWithBaseCase.types rename to tests/baselines/reference/simpleRecursionWithBaseCase1.types index e80cb0852389a..e05a9c8542267 100644 --- a/tests/baselines/reference/simpleRecursionWithBaseCase.types +++ b/tests/baselines/reference/simpleRecursionWithBaseCase1.types @@ -1,6 +1,6 @@ -//// [tests/cases/compiler/simpleRecursionWithBaseCase.ts] //// +//// [tests/cases/compiler/simpleRecursionWithBaseCase1.ts] //// -=== simpleRecursionWithBaseCase.ts === +=== simpleRecursionWithBaseCase1.ts === function fn1(n: number) { >fn1 : (n: number) => number >n : number diff --git a/tests/baselines/reference/simpleRecursionWithBaseCase2.errors.txt b/tests/baselines/reference/simpleRecursionWithBaseCase2.errors.txt new file mode 100644 index 0000000000000..8b70b1d294842 --- /dev/null +++ b/tests/baselines/reference/simpleRecursionWithBaseCase2.errors.txt @@ -0,0 +1,69 @@ +error TS2468: Cannot find global value 'Promise'. +simpleRecursionWithBaseCase2.ts(17,16): error TS2705: An async function or method in ES5/ES3 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your '--lib' option. +simpleRecursionWithBaseCase2.ts(21,16): error TS2705: An async function or method in ES5/ES3 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your '--lib' option. + + +!!! error TS2468: Cannot find global value 'Promise'. +==== simpleRecursionWithBaseCase2.ts (2 errors) ==== + async function rec1() { + if (Math.random() < 0.5) { + return rec1(); + } else { + return "hello"; + } + } + + async function rec2() { + if (Math.random() < 0.5) { + return await rec2(); + } else { + return "hello"; + } + } + + async function rec3() { + ~~~~ +!!! error TS2705: An async function or method in ES5/ES3 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your '--lib' option. + return rec3(); + } + + async function rec4() { + ~~~~ +!!! error TS2705: An async function or method in ES5/ES3 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your '--lib' option. + return await rec4(); + } + + async function rec5() { + if (Math.random() < 0.5) { + return ((rec1())); + } else { + return "hello"; + } + } + + async function rec6() { + if (Math.random() < 0.5) { + return await ((rec1())); + } else { + return "hello"; + } + } + + declare const ps: Promise | number; + + async function foo1() { + if (Math.random() > 0.5) { + return ps; + } else { + return await foo1(); + } + } + + async function foo2() { + if (Math.random() > 0.5) { + return ps; + } else { + return foo2(); + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/simpleRecursionWithBaseCase2.symbols b/tests/baselines/reference/simpleRecursionWithBaseCase2.symbols new file mode 100644 index 0000000000000..972c74f69d7ac --- /dev/null +++ b/tests/baselines/reference/simpleRecursionWithBaseCase2.symbols @@ -0,0 +1,119 @@ +//// [tests/cases/compiler/simpleRecursionWithBaseCase2.ts] //// + +=== simpleRecursionWithBaseCase2.ts === +async function rec1() { +>rec1 : Symbol(rec1, Decl(simpleRecursionWithBaseCase2.ts, 0, 0)) + + if (Math.random() < 0.5) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + return rec1(); +>rec1 : Symbol(rec1, Decl(simpleRecursionWithBaseCase2.ts, 0, 0)) + + } else { + return "hello"; + } +} + +async function rec2() { +>rec2 : Symbol(rec2, Decl(simpleRecursionWithBaseCase2.ts, 6, 1)) + + if (Math.random() < 0.5) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + return await rec2(); +>rec2 : Symbol(rec2, Decl(simpleRecursionWithBaseCase2.ts, 6, 1)) + + } else { + return "hello"; + } +} + +async function rec3() { +>rec3 : Symbol(rec3, Decl(simpleRecursionWithBaseCase2.ts, 14, 1)) + + return rec3(); +>rec3 : Symbol(rec3, Decl(simpleRecursionWithBaseCase2.ts, 14, 1)) +} + +async function rec4() { +>rec4 : Symbol(rec4, Decl(simpleRecursionWithBaseCase2.ts, 18, 1)) + + return await rec4(); +>rec4 : Symbol(rec4, Decl(simpleRecursionWithBaseCase2.ts, 18, 1)) +} + +async function rec5() { +>rec5 : Symbol(rec5, Decl(simpleRecursionWithBaseCase2.ts, 22, 1)) + + if (Math.random() < 0.5) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + return ((rec1())); +>rec1 : Symbol(rec1, Decl(simpleRecursionWithBaseCase2.ts, 0, 0)) + + } else { + return "hello"; + } +} + +async function rec6() { +>rec6 : Symbol(rec6, Decl(simpleRecursionWithBaseCase2.ts, 30, 1)) + + if (Math.random() < 0.5) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + return await ((rec1())); +>rec1 : Symbol(rec1, Decl(simpleRecursionWithBaseCase2.ts, 0, 0)) + + } else { + return "hello"; + } +} + +declare const ps: Promise | number; +>ps : Symbol(ps, Decl(simpleRecursionWithBaseCase2.ts, 40, 13)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) + +async function foo1() { +>foo1 : Symbol(foo1, Decl(simpleRecursionWithBaseCase2.ts, 40, 43)) + + if (Math.random() > 0.5) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + return ps; +>ps : Symbol(ps, Decl(simpleRecursionWithBaseCase2.ts, 40, 13)) + + } else { + return await foo1(); +>foo1 : Symbol(foo1, Decl(simpleRecursionWithBaseCase2.ts, 40, 43)) + } +} + +async function foo2() { +>foo2 : Symbol(foo2, Decl(simpleRecursionWithBaseCase2.ts, 48, 1)) + + if (Math.random() > 0.5) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + return ps; +>ps : Symbol(ps, Decl(simpleRecursionWithBaseCase2.ts, 40, 13)) + + } else { + return foo2(); +>foo2 : Symbol(foo2, Decl(simpleRecursionWithBaseCase2.ts, 48, 1)) + } +} + diff --git a/tests/baselines/reference/simpleRecursionWithBaseCase2.types b/tests/baselines/reference/simpleRecursionWithBaseCase2.types new file mode 100644 index 0000000000000..2afe3c81fe794 --- /dev/null +++ b/tests/baselines/reference/simpleRecursionWithBaseCase2.types @@ -0,0 +1,156 @@ +//// [tests/cases/compiler/simpleRecursionWithBaseCase2.ts] //// + +=== simpleRecursionWithBaseCase2.ts === +async function rec1() { +>rec1 : () => Promise + + if (Math.random() < 0.5) { +>Math.random() < 0.5 : boolean +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>0.5 : 0.5 + + return rec1(); +>rec1() : Promise +>rec1 : () => Promise + + } else { + return "hello"; +>"hello" : "hello" + } +} + +async function rec2() { +>rec2 : () => Promise + + if (Math.random() < 0.5) { +>Math.random() < 0.5 : boolean +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>0.5 : 0.5 + + return await rec2(); +>await rec2() : string +>rec2() : Promise +>rec2 : () => Promise + + } else { + return "hello"; +>"hello" : "hello" + } +} + +async function rec3() { +>rec3 : () => Promise + + return rec3(); +>rec3() : Promise +>rec3 : () => Promise +} + +async function rec4() { +>rec4 : () => Promise + + return await rec4(); +>await rec4() : never +>rec4() : Promise +>rec4 : () => Promise +} + +async function rec5() { +>rec5 : () => Promise + + if (Math.random() < 0.5) { +>Math.random() < 0.5 : boolean +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>0.5 : 0.5 + + return ((rec1())); +>((rec1())) : Promise +>(rec1()) : Promise +>rec1() : Promise +>rec1 : () => Promise + + } else { + return "hello"; +>"hello" : "hello" + } +} + +async function rec6() { +>rec6 : () => Promise + + if (Math.random() < 0.5) { +>Math.random() < 0.5 : boolean +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>0.5 : 0.5 + + return await ((rec1())); +>await ((rec1())) : string +>((rec1())) : Promise +>(rec1()) : Promise +>rec1() : Promise +>rec1 : () => Promise + + } else { + return "hello"; +>"hello" : "hello" + } +} + +declare const ps: Promise | number; +>ps : number | Promise + +async function foo1() { +>foo1 : () => Promise + + if (Math.random() > 0.5) { +>Math.random() > 0.5 : boolean +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>0.5 : 0.5 + + return ps; +>ps : number | Promise + + } else { + return await foo1(); +>await foo1() : string | number +>foo1() : Promise +>foo1 : () => Promise + } +} + +async function foo2() { +>foo2 : () => Promise + + if (Math.random() > 0.5) { +>Math.random() > 0.5 : boolean +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>0.5 : 0.5 + + return ps; +>ps : number | Promise + + } else { + return foo2(); +>foo2() : Promise +>foo2 : () => Promise + } +} + diff --git a/tests/cases/compiler/simpleRecursionWithBaseCase.ts b/tests/cases/compiler/simpleRecursionWithBaseCase1.ts similarity index 100% rename from tests/cases/compiler/simpleRecursionWithBaseCase.ts rename to tests/cases/compiler/simpleRecursionWithBaseCase1.ts diff --git a/tests/cases/compiler/simpleRecursionWithBaseCase2.ts b/tests/cases/compiler/simpleRecursionWithBaseCase2.ts new file mode 100644 index 0000000000000..762654b277df0 --- /dev/null +++ b/tests/cases/compiler/simpleRecursionWithBaseCase2.ts @@ -0,0 +1,61 @@ +// @strict: true +// @noImplicitAny: true +// @noEmit: true + +async function rec1() { + if (Math.random() < 0.5) { + return rec1(); + } else { + return "hello"; + } +} + +async function rec2() { + if (Math.random() < 0.5) { + return await rec2(); + } else { + return "hello"; + } +} + +async function rec3() { + return rec3(); +} + +async function rec4() { + return await rec4(); +} + +async function rec5() { + if (Math.random() < 0.5) { + return ((rec1())); + } else { + return "hello"; + } +} + +async function rec6() { + if (Math.random() < 0.5) { + return await ((rec1())); + } else { + return "hello"; + } +} + +declare const ps: Promise | number; + +async function foo1() { + if (Math.random() > 0.5) { + return ps; + } else { + return await foo1(); + } +} + +async function foo2() { + if (Math.random() > 0.5) { + return ps; + } else { + return foo2(); + } +} From 021f6cd9480caecd114b3a5ae5f894061f56f189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 9 Oct 2023 22:31:16 +0200 Subject: [PATCH 2/3] fixed jsdoc casts --- src/compiler/checker.ts | 4 +-- ...thesizedJSDocCastAtReturnStatement.symbols | 25 +++++++++++++++ ...enthesizedJSDocCastAtReturnStatement.types | 31 +++++++++++++++++++ ...parenthesizedJSDocCastAtReturnStatement.ts | 20 ++++++++++++ 4 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/parenthesizedJSDocCastAtReturnStatement.symbols create mode 100644 tests/baselines/reference/parenthesizedJSDocCastAtReturnStatement.types create mode 100644 tests/cases/compiler/parenthesizedJSDocCastAtReturnStatement.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 374e3b1f3d1b4..f9d139f7deeb2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -36676,11 +36676,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { forEachReturnStatement(func.body as Block, returnStatement => { let expr = returnStatement.expression; if (expr) { - expr = skipParentheses(expr); + expr = skipParentheses(expr, /*excludeJSDocTypeAssertions*/ true); // Bare calls to this same function don't contribute to inference // and `return await` is also safe to unwrap here if (functionFlags & FunctionFlags.Async && expr.kind === SyntaxKind.AwaitExpression) { - expr = skipParentheses((expr as AwaitExpression).expression); + expr = skipParentheses((expr as AwaitExpression).expression, /*excludeJSDocTypeAssertions*/ true); } if ( expr.kind === SyntaxKind.CallExpression && diff --git a/tests/baselines/reference/parenthesizedJSDocCastAtReturnStatement.symbols b/tests/baselines/reference/parenthesizedJSDocCastAtReturnStatement.symbols new file mode 100644 index 0000000000000..0bc8cf09402e8 --- /dev/null +++ b/tests/baselines/reference/parenthesizedJSDocCastAtReturnStatement.symbols @@ -0,0 +1,25 @@ +//// [tests/cases/compiler/parenthesizedJSDocCastAtReturnStatement.ts] //// + +=== index.js === +/** @type {Map>} */ +const cache = new Map() +>cache : Symbol(cache, Decl(index.js, 1, 5)) +>Map : Symbol(Map, Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) + +/** + * @param {string} key + * @returns {() => string} + */ +const getStringGetter = (key) => { +>getStringGetter : Symbol(getStringGetter, Decl(index.js, 7, 5)) +>key : Symbol(key, Decl(index.js, 7, 25)) + + return () => { + return /** @type {string} */ (cache.get(key)) +>cache.get : Symbol(Map.get, Decl(lib.es2015.collection.d.ts, --, --)) +>cache : Symbol(cache, Decl(index.js, 1, 5)) +>get : Symbol(Map.get, Decl(lib.es2015.collection.d.ts, --, --)) +>key : Symbol(key, Decl(index.js, 7, 25)) + } +} + diff --git a/tests/baselines/reference/parenthesizedJSDocCastAtReturnStatement.types b/tests/baselines/reference/parenthesizedJSDocCastAtReturnStatement.types new file mode 100644 index 0000000000000..cc1d5a66f92f9 --- /dev/null +++ b/tests/baselines/reference/parenthesizedJSDocCastAtReturnStatement.types @@ -0,0 +1,31 @@ +//// [tests/cases/compiler/parenthesizedJSDocCastAtReturnStatement.ts] //// + +=== index.js === +/** @type {Map>} */ +const cache = new Map() +>cache : Map> +>new Map() : Map +>Map : MapConstructor + +/** + * @param {string} key + * @returns {() => string} + */ +const getStringGetter = (key) => { +>getStringGetter : (key: string) => () => string +>(key) => { return () => { return /** @type {string} */ (cache.get(key)) }} : (key: string) => () => string +>key : string + + return () => { +>() => { return /** @type {string} */ (cache.get(key)) } : () => string + + return /** @type {string} */ (cache.get(key)) +>(cache.get(key)) : string +>cache.get(key) : string | Set | undefined +>cache.get : (key: string) => string | Set | undefined +>cache : Map> +>get : (key: string) => string | Set | undefined +>key : string + } +} + diff --git a/tests/cases/compiler/parenthesizedJSDocCastAtReturnStatement.ts b/tests/cases/compiler/parenthesizedJSDocCastAtReturnStatement.ts new file mode 100644 index 0000000000000..d6cdd49f42f9c --- /dev/null +++ b/tests/cases/compiler/parenthesizedJSDocCastAtReturnStatement.ts @@ -0,0 +1,20 @@ +// @strict: true +// @lib: esnext +// @noEmit: true +// @checkJs: true +// @allowJs: true + +// @filename: index.js + +/** @type {Map>} */ +const cache = new Map() + +/** + * @param {string} key + * @returns {() => string} + */ +const getStringGetter = (key) => { + return () => { + return /** @type {string} */ (cache.get(key)) + } +} From cd61223797a510884ca2f829c1f599b02cb49562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 9 Oct 2023 22:33:47 +0200 Subject: [PATCH 3/3] add correct lib setting to the added test case --- .../simpleRecursionWithBaseCase2.errors.txt | 69 ------------------- .../simpleRecursionWithBaseCase2.symbols | 14 ++-- .../compiler/simpleRecursionWithBaseCase2.ts | 1 + 3 files changed, 8 insertions(+), 76 deletions(-) delete mode 100644 tests/baselines/reference/simpleRecursionWithBaseCase2.errors.txt diff --git a/tests/baselines/reference/simpleRecursionWithBaseCase2.errors.txt b/tests/baselines/reference/simpleRecursionWithBaseCase2.errors.txt deleted file mode 100644 index 8b70b1d294842..0000000000000 --- a/tests/baselines/reference/simpleRecursionWithBaseCase2.errors.txt +++ /dev/null @@ -1,69 +0,0 @@ -error TS2468: Cannot find global value 'Promise'. -simpleRecursionWithBaseCase2.ts(17,16): error TS2705: An async function or method in ES5/ES3 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your '--lib' option. -simpleRecursionWithBaseCase2.ts(21,16): error TS2705: An async function or method in ES5/ES3 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your '--lib' option. - - -!!! error TS2468: Cannot find global value 'Promise'. -==== simpleRecursionWithBaseCase2.ts (2 errors) ==== - async function rec1() { - if (Math.random() < 0.5) { - return rec1(); - } else { - return "hello"; - } - } - - async function rec2() { - if (Math.random() < 0.5) { - return await rec2(); - } else { - return "hello"; - } - } - - async function rec3() { - ~~~~ -!!! error TS2705: An async function or method in ES5/ES3 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your '--lib' option. - return rec3(); - } - - async function rec4() { - ~~~~ -!!! error TS2705: An async function or method in ES5/ES3 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your '--lib' option. - return await rec4(); - } - - async function rec5() { - if (Math.random() < 0.5) { - return ((rec1())); - } else { - return "hello"; - } - } - - async function rec6() { - if (Math.random() < 0.5) { - return await ((rec1())); - } else { - return "hello"; - } - } - - declare const ps: Promise | number; - - async function foo1() { - if (Math.random() > 0.5) { - return ps; - } else { - return await foo1(); - } - } - - async function foo2() { - if (Math.random() > 0.5) { - return ps; - } else { - return foo2(); - } - } - \ No newline at end of file diff --git a/tests/baselines/reference/simpleRecursionWithBaseCase2.symbols b/tests/baselines/reference/simpleRecursionWithBaseCase2.symbols index 972c74f69d7ac..5d1360373648a 100644 --- a/tests/baselines/reference/simpleRecursionWithBaseCase2.symbols +++ b/tests/baselines/reference/simpleRecursionWithBaseCase2.symbols @@ -6,7 +6,7 @@ async function rec1() { if (Math.random() < 0.5) { >Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) ->Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) >random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) return rec1(); @@ -22,7 +22,7 @@ async function rec2() { if (Math.random() < 0.5) { >Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) ->Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) >random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) return await rec2(); @@ -52,7 +52,7 @@ async function rec5() { if (Math.random() < 0.5) { >Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) ->Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) >random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) return ((rec1())); @@ -68,7 +68,7 @@ async function rec6() { if (Math.random() < 0.5) { >Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) ->Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) >random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) return await ((rec1())); @@ -81,14 +81,14 @@ async function rec6() { declare const ps: Promise | number; >ps : Symbol(ps, Decl(simpleRecursionWithBaseCase2.ts, 40, 13)) ->Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) async function foo1() { >foo1 : Symbol(foo1, Decl(simpleRecursionWithBaseCase2.ts, 40, 43)) if (Math.random() > 0.5) { >Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) ->Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) >random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) return ps; @@ -105,7 +105,7 @@ async function foo2() { if (Math.random() > 0.5) { >Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) ->Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) >random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) return ps; diff --git a/tests/cases/compiler/simpleRecursionWithBaseCase2.ts b/tests/cases/compiler/simpleRecursionWithBaseCase2.ts index 762654b277df0..b5ab3ac417d18 100644 --- a/tests/cases/compiler/simpleRecursionWithBaseCase2.ts +++ b/tests/cases/compiler/simpleRecursionWithBaseCase2.ts @@ -1,5 +1,6 @@ // @strict: true // @noImplicitAny: true +// @lib: esnext // @noEmit: true async function rec1() {