From ef2d4650473493b3f26a0b06dfa6fba1135b1dfe Mon Sep 17 00:00:00 2001 From: Dan Vanderkam Date: Fri, 23 Feb 2024 10:51:44 -0500 Subject: [PATCH] Various fixes for circularity issue --- src/compiler/checker.ts | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 69e904183e6..33465ce7a2c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15466,7 +15466,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { else if (signature.declaration && isFunctionLikeDeclaration(signature.declaration) && (!signature.resolvedReturnType || signature.resolvedReturnType === booleanType)) { const { declaration } = signature; signature.resolvedTypePredicate = noTypePredicate; // avoid infinite loop - signature.resolvedTypePredicate = getTypePredicateFromBody(declaration, signature) || noTypePredicate; + signature.resolvedTypePredicate = getTypePredicateFromBody(declaration) || noTypePredicate; } else { signature.resolvedTypePredicate = noTypePredicate; @@ -37399,9 +37399,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function getTypePredicateFromBody(func: FunctionLikeDeclaration, _sig: Signature): TypePredicate | undefined { + function getTypePredicateFromBody(func: FunctionLikeDeclaration): TypePredicate | undefined { + switch (func.kind) { + case SyntaxKind.Constructor: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return undefined; + } const functionFlags = getFunctionFlags(func); - if (functionFlags !== FunctionFlags.Normal) return undefined; + if (functionFlags !== FunctionFlags.Normal || func.parameters.length === 0) return undefined; // Only attempt to infer a type predicate if there's exactly one return. let singleReturn: Expression | undefined; @@ -37418,6 +37424,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (bailedEarly || !singleReturn) return undefined; } + if (isTriviallyNonBoolean(singleReturn)) return undefined; + const predicate = checkIfExpressionRefinesAnyParameter(singleReturn); if (predicate) { const [i, type] = predicate; @@ -37431,7 +37439,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function checkIfExpressionRefinesAnyParameter(expr: Expression): [number, Type] | undefined { expr = skipParentheses(expr, /*excludeJSDocTypeAssertions*/ true); - const type = checkExpressionCached(expr, CheckMode.TypeOnly); + const type = checkExpressionCached(expr); // , CheckMode.TypeOnly); if (type !== booleanType || !func.body) return undefined; return forEach(func.parameters, (param, i) => { @@ -37477,6 +37485,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return trueType; } } + + // This bypasses the call to checkExpression for expressions that are clearly not booleans. + // In addition to potentially saving work, this avoids some circularlity issues. + function isTriviallyNonBoolean(expr: Expression): boolean { + if (isLiteralExpression(expr) || isLiteralExpressionOfObject(expr)) { + return true; + } + if (isIdentifier(expr)) { + const sym = getResolvedSymbol(expr); + if (sym.flags & (SymbolFlags.Class | SymbolFlags.ObjectLiteral | SymbolFlags.Function | SymbolFlags.Enum | SymbolFlags.EnumMember)) { + return true; + } + } + return false; // may or may not be boolean + } } /**