Skip to content

Commit 521f8a8

Browse files
authored
Ignore awaited self tail calls when collecting the return type of an async function (#56020)
1 parent 08d6df0 commit 521f8a8

13 files changed

+499
-81
lines changed

src/compiler/checker.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -36674,9 +36674,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3667436674
let hasReturnWithNoExpression = functionHasImplicitReturn(func);
3667536675
let hasReturnOfTypeNever = false;
3667636676
forEachReturnStatement(func.body as Block, returnStatement => {
36677-
const expr = returnStatement.expression;
36677+
let expr = returnStatement.expression;
3667836678
if (expr) {
36679+
expr = skipParentheses(expr, /*excludeJSDocTypeAssertions*/ true);
3667936680
// Bare calls to this same function don't contribute to inference
36681+
// and `return await` is also safe to unwrap here
36682+
if (functionFlags & FunctionFlags.Async && expr.kind === SyntaxKind.AwaitExpression) {
36683+
expr = skipParentheses((expr as AwaitExpression).expression, /*excludeJSDocTypeAssertions*/ true);
36684+
}
3668036685
if (
3668136686
expr.kind === SyntaxKind.CallExpression &&
3668236687
(expr as CallExpression).expression.kind === SyntaxKind.Identifier &&
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//// [tests/cases/compiler/parenthesizedJSDocCastAtReturnStatement.ts] ////
2+
3+
=== index.js ===
4+
/** @type {Map<string, string | Set<string>>} */
5+
const cache = new Map()
6+
>cache : Symbol(cache, Decl(index.js, 1, 5))
7+
>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, --, --))
8+
9+
/**
10+
* @param {string} key
11+
* @returns {() => string}
12+
*/
13+
const getStringGetter = (key) => {
14+
>getStringGetter : Symbol(getStringGetter, Decl(index.js, 7, 5))
15+
>key : Symbol(key, Decl(index.js, 7, 25))
16+
17+
return () => {
18+
return /** @type {string} */ (cache.get(key))
19+
>cache.get : Symbol(Map.get, Decl(lib.es2015.collection.d.ts, --, --))
20+
>cache : Symbol(cache, Decl(index.js, 1, 5))
21+
>get : Symbol(Map.get, Decl(lib.es2015.collection.d.ts, --, --))
22+
>key : Symbol(key, Decl(index.js, 7, 25))
23+
}
24+
}
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//// [tests/cases/compiler/parenthesizedJSDocCastAtReturnStatement.ts] ////
2+
3+
=== index.js ===
4+
/** @type {Map<string, string | Set<string>>} */
5+
const cache = new Map()
6+
>cache : Map<string, string | Set<string>>
7+
>new Map() : Map<any, any>
8+
>Map : MapConstructor
9+
10+
/**
11+
* @param {string} key
12+
* @returns {() => string}
13+
*/
14+
const getStringGetter = (key) => {
15+
>getStringGetter : (key: string) => () => string
16+
>(key) => { return () => { return /** @type {string} */ (cache.get(key)) }} : (key: string) => () => string
17+
>key : string
18+
19+
return () => {
20+
>() => { return /** @type {string} */ (cache.get(key)) } : () => string
21+
22+
return /** @type {string} */ (cache.get(key))
23+
>(cache.get(key)) : string
24+
>cache.get(key) : string | Set<string> | undefined
25+
>cache.get : (key: string) => string | Set<string> | undefined
26+
>cache : Map<string, string | Set<string>>
27+
>get : (key: string) => string | Set<string> | undefined
28+
>key : string
29+
}
30+
}
31+

tests/baselines/reference/simpleRecursionWithBaseCase.symbols

-67
This file was deleted.

tests/baselines/reference/simpleRecursionWithBaseCase.errors.txt tests/baselines/reference/simpleRecursionWithBaseCase1.errors.txt

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
simpleRecursionWithBaseCase.ts(8,21): error TS2554: Expected 1 arguments, but got 0.
2-
simpleRecursionWithBaseCase.ts(13,20): error TS2554: Expected 1 arguments, but got 0.
3-
simpleRecursionWithBaseCase.ts(19,20): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
4-
simpleRecursionWithBaseCase.ts(27,16): error TS2304: Cannot find name 'notfoundsymbol'.
5-
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.
1+
simpleRecursionWithBaseCase1.ts(8,21): error TS2554: Expected 1 arguments, but got 0.
2+
simpleRecursionWithBaseCase1.ts(13,20): error TS2554: Expected 1 arguments, but got 0.
3+
simpleRecursionWithBaseCase1.ts(19,20): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
4+
simpleRecursionWithBaseCase1.ts(27,16): error TS2304: Cannot find name 'notfoundsymbol'.
5+
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.
66

77

8-
==== simpleRecursionWithBaseCase.ts (5 errors) ====
8+
==== simpleRecursionWithBaseCase1.ts (5 errors) ====
99
function fn1(n: number) {
1010
if (n === 0) {
1111
return 3;
@@ -16,15 +16,15 @@ simpleRecursionWithBaseCase.ts(31,10): error TS7023: 'fn5' implicitly has return
1616
const num: number = fn1();
1717
~~~~~
1818
!!! error TS2554: Expected 1 arguments, but got 0.
19-
!!! related TS6210 simpleRecursionWithBaseCase.ts:1:14: An argument for 'n' was not provided.
19+
!!! related TS6210 simpleRecursionWithBaseCase1.ts:1:14: An argument for 'n' was not provided.
2020

2121
function fn2(n: number) {
2222
return fn2(n);
2323
}
2424
const nev: never = fn2();
2525
~~~~~
2626
!!! error TS2554: Expected 1 arguments, but got 0.
27-
!!! related TS6210 simpleRecursionWithBaseCase.ts:10:14: An argument for 'n' was not provided.
27+
!!! related TS6210 simpleRecursionWithBaseCase1.ts:10:14: An argument for 'n' was not provided.
2828

2929
function fn3(n: number) {
3030
if (n === 0) {

tests/baselines/reference/simpleRecursionWithBaseCase.js tests/baselines/reference/simpleRecursionWithBaseCase1.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
//// [tests/cases/compiler/simpleRecursionWithBaseCase.ts] ////
1+
//// [tests/cases/compiler/simpleRecursionWithBaseCase1.ts] ////
22

3-
//// [simpleRecursionWithBaseCase.ts]
3+
//// [simpleRecursionWithBaseCase1.ts]
44
function fn1(n: number) {
55
if (n === 0) {
66
return 3;
@@ -36,7 +36,7 @@ function fn5() {
3636
}
3737

3838

39-
//// [simpleRecursionWithBaseCase.js]
39+
//// [simpleRecursionWithBaseCase1.js]
4040
"use strict";
4141
function fn1(n) {
4242
if (n === 0) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//// [tests/cases/compiler/simpleRecursionWithBaseCase1.ts] ////
2+
3+
=== simpleRecursionWithBaseCase1.ts ===
4+
function fn1(n: number) {
5+
>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase1.ts, 0, 0))
6+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 0, 13))
7+
8+
if (n === 0) {
9+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 0, 13))
10+
11+
return 3;
12+
} else {
13+
return fn1(n - 1);
14+
>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase1.ts, 0, 0))
15+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 0, 13))
16+
}
17+
}
18+
const num: number = fn1();
19+
>num : Symbol(num, Decl(simpleRecursionWithBaseCase1.ts, 7, 5))
20+
>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase1.ts, 0, 0))
21+
22+
function fn2(n: number) {
23+
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase1.ts, 7, 26))
24+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 9, 13))
25+
26+
return fn2(n);
27+
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase1.ts, 7, 26))
28+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 9, 13))
29+
}
30+
const nev: never = fn2();
31+
>nev : Symbol(nev, Decl(simpleRecursionWithBaseCase1.ts, 12, 5))
32+
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase1.ts, 7, 26))
33+
34+
function fn3(n: number) {
35+
>fn3 : Symbol(fn3, Decl(simpleRecursionWithBaseCase1.ts, 12, 25))
36+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 14, 13))
37+
38+
if (n === 0) {
39+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 14, 13))
40+
41+
return 3;
42+
} else {
43+
return fn1("hello world");
44+
>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase1.ts, 0, 0))
45+
}
46+
}
47+
48+
function fn4(n: number) {
49+
>fn4 : Symbol(fn4, Decl(simpleRecursionWithBaseCase1.ts, 20, 1))
50+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 22, 13))
51+
52+
if (n === 0) {
53+
>n : Symbol(n, Decl(simpleRecursionWithBaseCase1.ts, 22, 13))
54+
55+
return 3;
56+
} else {
57+
return notfoundsymbol("hello world");
58+
}
59+
}
60+
61+
function fn5() {
62+
>fn5 : Symbol(fn5, Decl(simpleRecursionWithBaseCase1.ts, 28, 1))
63+
64+
return [fn5][0]();
65+
>fn5 : Symbol(fn5, Decl(simpleRecursionWithBaseCase1.ts, 28, 1))
66+
}
67+

tests/baselines/reference/simpleRecursionWithBaseCase.types tests/baselines/reference/simpleRecursionWithBaseCase1.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
//// [tests/cases/compiler/simpleRecursionWithBaseCase.ts] ////
1+
//// [tests/cases/compiler/simpleRecursionWithBaseCase1.ts] ////
22

3-
=== simpleRecursionWithBaseCase.ts ===
3+
=== simpleRecursionWithBaseCase1.ts ===
44
function fn1(n: number) {
55
>fn1 : (n: number) => number
66
>n : number
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//// [tests/cases/compiler/simpleRecursionWithBaseCase2.ts] ////
2+
3+
=== simpleRecursionWithBaseCase2.ts ===
4+
async function rec1() {
5+
>rec1 : Symbol(rec1, Decl(simpleRecursionWithBaseCase2.ts, 0, 0))
6+
7+
if (Math.random() < 0.5) {
8+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
9+
>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, --, --))
10+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
11+
12+
return rec1();
13+
>rec1 : Symbol(rec1, Decl(simpleRecursionWithBaseCase2.ts, 0, 0))
14+
15+
} else {
16+
return "hello";
17+
}
18+
}
19+
20+
async function rec2() {
21+
>rec2 : Symbol(rec2, Decl(simpleRecursionWithBaseCase2.ts, 6, 1))
22+
23+
if (Math.random() < 0.5) {
24+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
25+
>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, --, --))
26+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
27+
28+
return await rec2();
29+
>rec2 : Symbol(rec2, Decl(simpleRecursionWithBaseCase2.ts, 6, 1))
30+
31+
} else {
32+
return "hello";
33+
}
34+
}
35+
36+
async function rec3() {
37+
>rec3 : Symbol(rec3, Decl(simpleRecursionWithBaseCase2.ts, 14, 1))
38+
39+
return rec3();
40+
>rec3 : Symbol(rec3, Decl(simpleRecursionWithBaseCase2.ts, 14, 1))
41+
}
42+
43+
async function rec4() {
44+
>rec4 : Symbol(rec4, Decl(simpleRecursionWithBaseCase2.ts, 18, 1))
45+
46+
return await rec4();
47+
>rec4 : Symbol(rec4, Decl(simpleRecursionWithBaseCase2.ts, 18, 1))
48+
}
49+
50+
async function rec5() {
51+
>rec5 : Symbol(rec5, Decl(simpleRecursionWithBaseCase2.ts, 22, 1))
52+
53+
if (Math.random() < 0.5) {
54+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
55+
>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, --, --))
56+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
57+
58+
return ((rec1()));
59+
>rec1 : Symbol(rec1, Decl(simpleRecursionWithBaseCase2.ts, 0, 0))
60+
61+
} else {
62+
return "hello";
63+
}
64+
}
65+
66+
async function rec6() {
67+
>rec6 : Symbol(rec6, Decl(simpleRecursionWithBaseCase2.ts, 30, 1))
68+
69+
if (Math.random() < 0.5) {
70+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
71+
>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, --, --))
72+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
73+
74+
return await ((rec1()));
75+
>rec1 : Symbol(rec1, Decl(simpleRecursionWithBaseCase2.ts, 0, 0))
76+
77+
} else {
78+
return "hello";
79+
}
80+
}
81+
82+
declare const ps: Promise<string> | number;
83+
>ps : Symbol(ps, Decl(simpleRecursionWithBaseCase2.ts, 40, 13))
84+
>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, --, --))
85+
86+
async function foo1() {
87+
>foo1 : Symbol(foo1, Decl(simpleRecursionWithBaseCase2.ts, 40, 43))
88+
89+
if (Math.random() > 0.5) {
90+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
91+
>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, --, --))
92+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
93+
94+
return ps;
95+
>ps : Symbol(ps, Decl(simpleRecursionWithBaseCase2.ts, 40, 13))
96+
97+
} else {
98+
return await foo1();
99+
>foo1 : Symbol(foo1, Decl(simpleRecursionWithBaseCase2.ts, 40, 43))
100+
}
101+
}
102+
103+
async function foo2() {
104+
>foo2 : Symbol(foo2, Decl(simpleRecursionWithBaseCase2.ts, 48, 1))
105+
106+
if (Math.random() > 0.5) {
107+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
108+
>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, --, --))
109+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
110+
111+
return ps;
112+
>ps : Symbol(ps, Decl(simpleRecursionWithBaseCase2.ts, 40, 13))
113+
114+
} else {
115+
return foo2();
116+
>foo2 : Symbol(foo2, Decl(simpleRecursionWithBaseCase2.ts, 48, 1))
117+
}
118+
}
119+

0 commit comments

Comments
 (0)