Fix self tail call return type inference in assigned anonymous functions (#58124)

This commit is contained in:
Andrew Branch 2024-04-10 12:51:34 -07:00 committed by GitHub
parent 30095a225c
commit 2b038ff64a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 214 additions and 11 deletions

View File

@ -28086,7 +28086,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
case SyntaxKind.Identifier:
if (!isThisInTypeQuery(node)) {
const symbol = getResolvedSymbol(node as Identifier);
return isConstantVariable(symbol) || isParameterOrMutableLocalVariable(symbol) && !isSymbolAssigned(symbol);
return isConstantVariable(symbol)
|| isParameterOrMutableLocalVariable(symbol) && !isSymbolAssigned(symbol)
|| !!symbol.valueDeclaration && isFunctionExpression(symbol.valueDeclaration);
}
break;
case SyntaxKind.PropertyAccessExpression:
@ -37796,7 +37798,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (
expr.kind === SyntaxKind.CallExpression &&
(expr as CallExpression).expression.kind === SyntaxKind.Identifier &&
checkExpressionCached((expr as CallExpression).expression).symbol === func.symbol
checkExpressionCached((expr as CallExpression).expression).symbol === getMergedSymbol(func.symbol) &&
(!isFunctionExpressionOrArrowFunction(func.symbol.valueDeclaration!) || isConstantReference((expr as CallExpression).expression))
) {
hasReturnOfTypeNever = true;
return;

View File

@ -2,13 +2,14 @@ implicitAnyFromCircularInference.ts(2,5): error TS2502: 'a' is referenced direct
implicitAnyFromCircularInference.ts(5,5): error TS2502: 'b' is referenced directly or indirectly in its own type annotation.
implicitAnyFromCircularInference.ts(6,5): error TS2502: 'c' is referenced directly or indirectly in its own type annotation.
implicitAnyFromCircularInference.ts(9,5): error TS2502: 'd' is referenced directly or indirectly in its own type annotation.
implicitAnyFromCircularInference.ts(17,5): error TS7023: 'f1' 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.
implicitAnyFromCircularInference.ts(22,5): error TS7023: 'f2' 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.
implicitAnyFromCircularInference.ts(25,10): error TS7023: 'h' 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.
implicitAnyFromCircularInference.ts(27,14): error TS7023: 'foo' 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.
implicitAnyFromCircularInference.ts(44,9): error TS7023: 'x' 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.
==== implicitAnyFromCircularInference.ts (8 errors) ====
==== implicitAnyFromCircularInference.ts (9 errors) ====
// Error expected
var a: typeof a;
~
@ -34,6 +35,8 @@ implicitAnyFromCircularInference.ts(44,9): error TS7023: 'x' implicitly has retu
// Error expected
var f1 = function () {
~~
!!! error TS7023: 'f1' 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.
return f1();
};

View File

@ -45,16 +45,16 @@ function g() { return g(); }
// Error expected
var f1 = function () {
>f1 : () => never
> : ^^^^^^^^^^^
>function () { return f1();} : () => never
> :
>f1 : () => any
> : ^^^^^^^^^
>function () { return f1();} : () => any
> :
return f1();
>f1() : never
> : ^^^^^
>f1 : () => never
> : ^^^^^^^^^^^
>f1() : any
> : ^^^
>f1 : () => any
> : ^^^^^^^^^
};

View File

@ -0,0 +1,17 @@
//// [tests/cases/compiler/simpleRecursionWithBaseCase3.ts] ////
=== simpleRecursionWithBaseCase3.ts ===
const fn1 = () => {
>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase3.ts, 0, 5))
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 fn1()
>fn1 : Symbol(fn1, Decl(simpleRecursionWithBaseCase3.ts, 0, 5))
}
return 0
}

View File

@ -0,0 +1,34 @@
//// [tests/cases/compiler/simpleRecursionWithBaseCase3.ts] ////
=== simpleRecursionWithBaseCase3.ts ===
const fn1 = () => {
>fn1 : () => number
> : ^^^^^^^^^^^^
>() => { if (Math.random() > 0.5) { return fn1() } return 0} : () => number
> :
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 fn1()
>fn1() : number
> : ^^^^^^
>fn1 : () => number
> : ^^^^^^^^^^^^
}
return 0
>0 : 0
> : ^
}

View File

@ -0,0 +1,43 @@
//// [tests/cases/compiler/simpleRecursionWithBaseCase4.ts] ////
=== simpleRecursionWithBaseCase4.ts ===
var fn2 = function(name) {
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase4.ts, 0, 3))
>name : Symbol(name, Decl(simpleRecursionWithBaseCase4.ts, 0, 19))
fn2 = compose(this, 0, 1)
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase4.ts, 0, 3))
>compose : Symbol(compose, Decl(simpleRecursionWithBaseCase4.ts, 2, 18))
return fn2(name)
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase4.ts, 0, 3))
>name : Symbol(name, Decl(simpleRecursionWithBaseCase4.ts, 0, 19))
function compose(child, level, find) {
>compose : Symbol(compose, Decl(simpleRecursionWithBaseCase4.ts, 2, 18))
>child : Symbol(child, Decl(simpleRecursionWithBaseCase4.ts, 4, 19))
>level : Symbol(level, Decl(simpleRecursionWithBaseCase4.ts, 4, 25))
>find : Symbol(find, Decl(simpleRecursionWithBaseCase4.ts, 4, 32))
if (child === find) {
>child : Symbol(child, Decl(simpleRecursionWithBaseCase4.ts, 4, 19))
>find : Symbol(find, Decl(simpleRecursionWithBaseCase4.ts, 4, 32))
return level
>level : Symbol(level, Decl(simpleRecursionWithBaseCase4.ts, 4, 25))
}
return compose(child, level + 1, find)
>compose : Symbol(compose, Decl(simpleRecursionWithBaseCase4.ts, 2, 18))
>child : Symbol(child, Decl(simpleRecursionWithBaseCase4.ts, 4, 19))
>level : Symbol(level, Decl(simpleRecursionWithBaseCase4.ts, 4, 25))
>find : Symbol(find, Decl(simpleRecursionWithBaseCase4.ts, 4, 32))
}
}
var d = fn2(1); // d: any
>d : Symbol(d, Decl(simpleRecursionWithBaseCase4.ts, 12, 3))
>fn2 : Symbol(fn2, Decl(simpleRecursionWithBaseCase4.ts, 0, 3))
d.redefined();
>d : Symbol(d, Decl(simpleRecursionWithBaseCase4.ts, 12, 3))

View File

@ -0,0 +1,74 @@
//// [tests/cases/compiler/simpleRecursionWithBaseCase4.ts] ////
=== simpleRecursionWithBaseCase4.ts ===
var fn2 = function(name) {
>fn2 : (name: any) => any
> : ^ ^^^^^^^^^^^^^
>function(name) { fn2 = compose(this, 0, 1) return fn2(name) function compose(child, level, find) { if (child === find) { return level } return compose(child, level + 1, find) }} : (name: any) => any
> :
>name : any
fn2 = compose(this, 0, 1)
>fn2 = compose(this, 0, 1) : any
>fn2 : (name: any) => any
> : ^ ^^^^^^^^^^^^^
>compose(this, 0, 1) : any
>compose : (child: any, level: any, find: any) => any
> : ^ ^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^
>this : any
>0 : 0
> : ^
>1 : 1
> : ^
return fn2(name)
>fn2(name) : any
>fn2 : (name: any) => any
> : ^ ^^^^^^^^^^^^^
>name : any
function compose(child, level, find) {
>compose : (child: any, level: any, find: any) => any
> : ^ ^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^
>child : any
>level : any
>find : any
if (child === find) {
>child === find : boolean
> : ^^^^^^^
>child : any
>find : any
return level
>level : any
}
return compose(child, level + 1, find)
>compose(child, level + 1, find) : any
>compose : (child: any, level: any, find: any) => any
> : ^ ^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^
>child : any
>level + 1 : any
>level : any
>1 : 1
> : ^
>find : any
}
}
var d = fn2(1); // d: any
>d : any
>fn2(1) : any
>fn2 : (name: any) => any
> : ^ ^^^^^^^^^^^^^
>1 : 1
> : ^
d.redefined();
>d.redefined() : any
>d.redefined : any
>d : any
> : ^^^
>redefined : any
> : ^^^

View File

@ -0,0 +1,11 @@
// @strict: true
// @noImplicitAny: true
// @lib: esnext
// @noEmit: true
const fn1 = () => {
if (Math.random() > 0.5) {
return fn1()
}
return 0
}

View File

@ -0,0 +1,18 @@
// @checkJs: true
// @noEmit: true
// @strict: false
var fn2 = function(name) {
fn2 = compose(this, 0, 1)
return fn2(name)
function compose(child, level, find) {
if (child === find) {
return level
}
return compose(child, level + 1, find)
}
}
var d = fn2(1); // d: any
d.redefined();