diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 72b9e4acadedb..b01352968eb42 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1000,6 +1000,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { const saveExceptionTarget = currentExceptionTarget; const saveActiveLabelList = activeLabelList; const saveHasExplicitReturn = hasExplicitReturn; + const saveSeenThisKeyword = seenThisKeyword; const isImmediatelyInvoked = ( containerFlags & ContainerFlags.IsFunctionExpression && !hasSyntacticModifier(node, ModifierFlags.Async) && @@ -1022,19 +1023,22 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { currentContinueTarget = undefined; activeLabelList = undefined; hasExplicitReturn = false; + seenThisKeyword = false; bindChildren(node); - // Reset all reachability check related flags on node (for incremental scenarios) - node.flags &= ~NodeFlags.ReachabilityAndEmitFlags; + // Reset flags (for incremental scenarios) + node.flags &= ~(NodeFlags.ReachabilityAndEmitFlags | NodeFlags.ContainsThis); if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).body)) { node.flags |= NodeFlags.HasImplicitReturn; if (hasExplicitReturn) node.flags |= NodeFlags.HasExplicitReturn; (node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).endFlowNode = currentFlow; } + if (seenThisKeyword) { + node.flags |= NodeFlags.ContainsThis; + } if (node.kind === SyntaxKind.SourceFile) { node.flags |= emitFlags; (node as SourceFile).endFlowNode = currentFlow; } - if (currentReturnTarget) { addAntecedent(currentReturnTarget, currentFlow); currentFlow = finishFlowLabel(currentReturnTarget); @@ -1051,12 +1055,15 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { currentExceptionTarget = saveExceptionTarget; activeLabelList = saveActiveLabelList; hasExplicitReturn = saveHasExplicitReturn; + seenThisKeyword = node.kind === SyntaxKind.ArrowFunction ? saveSeenThisKeyword || seenThisKeyword : saveSeenThisKeyword; } else if (containerFlags & ContainerFlags.IsInterface) { + const saveSeenThisKeyword = seenThisKeyword; seenThisKeyword = false; bindChildren(node); Debug.assertNotNode(node, isIdentifier); // ContainsThis cannot overlap with HasExtendedUnicodeEscape on Identifier node.flags = seenThisKeyword ? node.flags | NodeFlags.ContainsThis : node.flags & ~NodeFlags.ContainsThis; + seenThisKeyword = saveSeenThisKeyword; } else { bindChildren(node); @@ -2852,6 +2859,9 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { } // falls through case SyntaxKind.ThisKeyword: + if (node.kind === SyntaxKind.ThisKeyword) { + seenThisKeyword = true; + } // TODO: Why use `isExpression` here? both Identifier and ThisKeyword are expressions. if (currentFlow && (isExpression(node) || parent.kind === SyntaxKind.ShorthandPropertyAssignment)) { (node as Identifier | ThisExpression).flowNode = currentFlow; @@ -3833,6 +3843,8 @@ export function getContainerFlags(node: Node): ContainerFlags { // falls through case SyntaxKind.Constructor: case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ClassStaticBlockDeclaration: + return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike; case SyntaxKind.MethodSignature: case SyntaxKind.CallSignature: case SyntaxKind.JSDocSignature: @@ -3840,12 +3852,11 @@ export function getContainerFlags(node: Node): ContainerFlags { case SyntaxKind.FunctionType: case SyntaxKind.ConstructSignature: case SyntaxKind.ConstructorType: - case SyntaxKind.ClassStaticBlockDeclaration: - return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike; + return ContainerFlags.IsContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike; case SyntaxKind.JSDocImportTag: // treat as a container to prevent using an enclosing effective host, ensuring import bindings are scoped correctly - return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals; + return ContainerFlags.IsContainer | ContainerFlags.HasLocals; case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: @@ -3853,8 +3864,6 @@ export function getContainerFlags(node: Node): ContainerFlags { case SyntaxKind.ModuleBlock: return ContainerFlags.IsControlFlowContainer; - case SyntaxKind.PropertyDeclaration: - return (node as PropertyDeclaration).initializer ? ContainerFlags.IsControlFlowContainer : 0; case SyntaxKind.CatchClause: case SyntaxKind.ForStatement: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 74663e107cc66..b2f82e335fbdd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21143,9 +21143,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const { initializer } = node as JsxAttribute; return !!initializer && isContextSensitive(initializer); } - case SyntaxKind.JsxExpression: { + case SyntaxKind.JsxExpression: + case SyntaxKind.YieldExpression: { // It is possible to that node.expression is undefined (e.g
) - const { expression } = node as JsxExpression; + const { expression } = node as JsxExpression | YieldExpression; return !!expression && isContextSensitive(expression); } } @@ -21154,7 +21155,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function isContextSensitiveFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean { - return hasContextSensitiveParameters(node) || hasContextSensitiveReturnExpression(node); + return hasContextSensitiveParameters(node) || hasContextSensitiveReturnExpression(node) || hasContextSensitiveYieldExpression(node); } function hasContextSensitiveReturnExpression(node: FunctionLikeDeclaration) { @@ -21167,6 +21168,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return !!forEachReturnStatement(node.body as Block, statement => !!statement.expression && isContextSensitive(statement.expression)); } + function hasContextSensitiveYieldExpression(node: FunctionLikeDeclaration): boolean { + // yield expressions can be context sensitive in situations like: + // + // declare function test(gen: () => Generator<(arg: number) => string, void, void>): void; + // + // test(function* () { + // yield (arg) => String(arg); + // }); + return !!(getFunctionFlags(node) & FunctionFlags.Generator && node.body && forEachYieldExpression(node.body as Block, isContextSensitive)); + } + function isContextSensitiveFunctionOrObjectLiteralMethod(func: Node): func is FunctionExpression | ArrowFunction | MethodDeclaration { return (isFunctionExpressionOrArrowFunction(func) || isObjectLiteralMethod(func)) && isContextSensitiveFunctionLikeDeclaration(func); @@ -32629,7 +32641,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (inferenceContext && contextFlags! & ContextFlags.Signature && some(inferenceContext.inferences, hasInferenceCandidatesOrDefault)) { // For contextual signatures we incorporate all inferences made so far, e.g. from return // types as well as arguments to the left in a function call. - return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper); + const type = instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper); + if (!(type.flags & TypeFlags.AnyOrUnknown)) { + return type; + } } if (inferenceContext?.returnMapper) { // For other purposes (e.g. determining whether to produce literal types) we only @@ -32637,9 +32652,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // the 'boolean' type from the contextual type such that contextually typed boolean // literals actually end up widening to 'boolean' (see #48363). const type = instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper); - return type.flags & TypeFlags.Union && containsType((type as UnionType).types, regularFalseType) && containsType((type as UnionType).types, regularTrueType) ? - filterType(type, t => t !== regularFalseType && t !== regularTrueType) : - type; + if (!(type.flags & TypeFlags.AnyOrUnknown)) { + return type.flags & TypeFlags.Union && containsType((type as UnionType).types, regularFalseType) && containsType((type as UnionType).types, regularTrueType) ? + filterType(type, t => t !== regularFalseType && t !== regularTrueType) : + type; + } } } return contextualType; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cdba2e880afc3..bdb213f622451 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6526,7 +6526,7 @@ export const enum ObjectFlags { /** @internal */ ContainsObjectOrArrayLiteral = 1 << 17, // Type is or contains object literal type /** @internal */ - NonInferrableType = 1 << 18, // Type is or contains anyFunctionType or silentNeverType + NonInferrableType = 1 << 18, // Type is or contains anyFunctionType or silentNeverType, or it's a context free `returnTypeOnly` /** @internal */ CouldContainTypeVariablesComputed = 1 << 19, // CouldContainTypeVariables flag has been computed /** @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index cdf898ca8c3fc..5562f9f736397 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2880,19 +2880,24 @@ export function forEachReturnStatement