Skip to content

Commit

Permalink
Ignore awaited self tail calls when collecting the return type of an …
Browse files Browse the repository at this point in the history
…async function (microsoft#56020)
  • Loading branch information
Andarist authored Oct 10, 2023
1 parent 08d6df0 commit 521f8a8
Show file tree
Hide file tree
Showing 13 changed files with 499 additions and 81 deletions.
7 changes: 6 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, /*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, /*excludeJSDocTypeAssertions*/ true);
}
if (
expr.kind === SyntaxKind.CallExpression &&
(expr as CallExpression).expression.kind === SyntaxKind.Identifier &&
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//// [tests/cases/compiler/parenthesizedJSDocCastAtReturnStatement.ts] ////

=== index.js ===
/** @type {Map<string, string | Set<string>>} */
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))
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//// [tests/cases/compiler/parenthesizedJSDocCastAtReturnStatement.ts] ////

=== index.js ===
/** @type {Map<string, string | Set<string>>} */
const cache = new Map()
>cache : Map<string, string | Set<string>>
>new Map() : Map<any, any>
>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<string> | undefined
>cache.get : (key: string) => string | Set<string> | undefined
>cache : Map<string, string | Set<string>>
>get : (key: string) => string | Set<string> | undefined
>key : string
}
}

67 changes: 0 additions & 67 deletions tests/baselines/reference/simpleRecursionWithBaseCase.symbols

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,15 +16,15 @@ 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);
}
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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -36,7 +36,7 @@ function fn5() {
}


//// [simpleRecursionWithBaseCase.js]
//// [simpleRecursionWithBaseCase1.js]
"use strict";
function fn1(n) {
if (n === 0) {
Expand Down
67 changes: 67 additions & 0 deletions tests/baselines/reference/simpleRecursionWithBaseCase1.symbols
Original file line number Diff line number Diff line change
@@ -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))
}

Original file line number Diff line number Diff line change
@@ -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
Expand Down
119 changes: 119 additions & 0 deletions tests/baselines/reference/simpleRecursionWithBaseCase2.symbols
Original file line number Diff line number Diff line change
@@ -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, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.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, --, --), 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();
>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, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.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, --, --), 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()));
>rec1 : Symbol(rec1, Decl(simpleRecursionWithBaseCase2.ts, 0, 0))

} else {
return "hello";
}
}

declare const ps: Promise<string> | number;
>ps : Symbol(ps, Decl(simpleRecursionWithBaseCase2.ts, 40, 13))
>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, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.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, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.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))
}
}

Loading

0 comments on commit 521f8a8

Please sign in to comment.