diff --git a/internal/ast/utilities.go b/internal/ast/utilities.go index 2cc3520e9a9..fbc59d31bac 100644 --- a/internal/ast/utilities.go +++ b/internal/ast/utilities.go @@ -3969,7 +3969,7 @@ func HasContextSensitiveParameters(node *Node) bool { // an implicit 'this' parameter which is subject to contextual typing. parameter := core.FirstOrNil(node.Parameters()) if parameter == nil || !IsThisParameter(parameter) { - return true + return node.Flags&NodeFlagsContainsThis != 0 } } } diff --git a/internal/binder/binder.go b/internal/binder/binder.go index f44ff210738..187c8e6a840 100644 --- a/internal/binder/binder.go +++ b/internal/binder/binder.go @@ -39,6 +39,7 @@ const ( ContainerFlagsIsInterface ContainerFlags = 1 << 6 ContainerFlagsIsObjectLiteralOrClassExpressionMethodOrAccessor ContainerFlags = 1 << 7 ContainerFlagsIsThisContainer ContainerFlags = 1 << 8 + ContainerFlagsPropagatesThisKeyword ContainerFlags = 1 << 9 ) type ExpandoAssignmentInfo struct { @@ -615,6 +616,9 @@ func (b *Binder) bind(node *ast.Node) bool { node.AsIdentifier().FlowNode = b.currentFlow b.checkContextualIdentifier(node) case ast.KindThisKeyword, ast.KindSuperKeyword: + if node.Kind == ast.KindThisKeyword { + b.seenThisKeyword = true + } node.AsKeywordExpression().FlowNode = b.currentFlow case ast.KindQualifiedName: if b.currentFlow != nil && ast.IsPartOfTypeQuery(node) { @@ -1520,6 +1524,7 @@ func (b *Binder) bindContainer(node *ast.Node, containerFlags ContainerFlags) { saveExceptionTarget := b.currentExceptionTarget saveActiveLabelList := b.activeLabelList saveHasExplicitReturn := b.hasExplicitReturn + saveSeenThisKeyword := b.seenThisKeyword isImmediatelyInvoked := (containerFlags&ContainerFlagsIsFunctionExpression != 0 && !ast.HasSyntacticModifier(node, ast.ModifierFlagsAsync) && !isGeneratorFunctionExpression(node) && @@ -1545,9 +1550,10 @@ func (b *Binder) bindContainer(node *ast.Node, containerFlags ContainerFlags) { b.currentContinueTarget = nil b.activeLabelList = nil b.hasExplicitReturn = false + b.seenThisKeyword = false b.bindChildren(node) - // Reset all reachability check related flags on node (for incremental scenarios) - node.Flags &= ^ast.NodeFlagsReachabilityCheckFlags + // Reset flags (for incremental scenarios) + node.Flags &= ^(ast.NodeFlagsReachabilityCheckFlags | ast.NodeFlagsContainsThis) if b.currentFlow.Flags&ast.FlowFlagsUnreachable == 0 && containerFlags&ContainerFlagsIsFunctionLike != 0 { bodyData := node.BodyData() if bodyData != nil && ast.NodeIsPresent(bodyData.Body) { @@ -1558,11 +1564,13 @@ func (b *Binder) bindContainer(node *ast.Node, containerFlags ContainerFlags) { bodyData.EndFlowNode = b.currentFlow } } + if b.seenThisKeyword { + node.Flags |= ast.NodeFlagsContainsThis + } if node.Kind == ast.KindSourceFile { node.Flags |= b.emitFlags node.AsSourceFile().EndFlowNode = b.currentFlow } - if b.currentReturnTarget != nil { b.addAntecedent(b.currentReturnTarget, b.currentFlow) b.currentFlow = b.finishFlowLabel(b.currentReturnTarget) @@ -1579,7 +1587,13 @@ func (b *Binder) bindContainer(node *ast.Node, containerFlags ContainerFlags) { b.currentExceptionTarget = saveExceptionTarget b.activeLabelList = saveActiveLabelList b.hasExplicitReturn = saveHasExplicitReturn + if containerFlags&ContainerFlagsPropagatesThisKeyword != 0 { + b.seenThisKeyword = saveSeenThisKeyword || b.seenThisKeyword + } else { + b.seenThisKeyword = saveSeenThisKeyword + } } else if containerFlags&ContainerFlagsIsInterface != 0 { + saveSeenThisKeyword := b.seenThisKeyword b.seenThisKeyword = false b.bindChildren(node) // ContainsThis cannot overlap with HasExtendedUnicodeEscape on Identifier @@ -1588,6 +1602,7 @@ func (b *Binder) bindContainer(node *ast.Node, containerFlags ContainerFlags) { } else { node.Flags &= ^ast.NodeFlagsContainsThis } + b.seenThisKeyword = saveSeenThisKeyword } else { b.bindChildren(node) } @@ -2525,16 +2540,14 @@ func GetContainerFlags(node *ast.Node) ContainerFlags { return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsObjectLiteralOrClassExpressionMethodOrAccessor | ContainerFlagsIsThisContainer } fallthrough - case ast.KindConstructor, ast.KindClassStaticBlockDeclaration: + case ast.KindConstructor, ast.KindFunctionDeclaration, ast.KindClassStaticBlockDeclaration: return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsThisContainer case ast.KindMethodSignature, ast.KindCallSignature, ast.KindFunctionType, ast.KindConstructSignature, ast.KindConstructorType: - return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike - case ast.KindFunctionDeclaration: - return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsThisContainer + return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsPropagatesThisKeyword case ast.KindFunctionExpression: return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsFunctionExpression | ContainerFlagsIsThisContainer case ast.KindArrowFunction: - return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsFunctionExpression + return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsFunctionExpression | ContainerFlagsPropagatesThisKeyword case ast.KindModuleBlock: return ContainerFlagsIsControlFlowContainer case ast.KindPropertyDeclaration: diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 96d1b4a1574..75503d12790 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -19849,10 +19849,10 @@ func mayReturnNever(fn *ast.Node) bool { func (c *Checker) checkAndAggregateYieldOperandTypes(fn *ast.Node, checkMode CheckMode) (yieldTypes []*Type, nextTypes []*Type) { isAsync := (getFunctionFlags(fn) & FunctionFlagsAsync) != 0 - forEachYieldExpression(fn.Body(), func(yieldExpr *ast.Node) { + forEachYieldExpression(fn.Body(), func(yieldExpr *ast.Node) bool { yieldExprType := c.undefinedWideningType if yieldExpr.Expression() != nil { - yieldExprType = c.checkExpressionEx(yieldExpr.Expression(), checkMode) + yieldExprType = c.checkExpressionEx(yieldExpr.Expression(), checkMode & ^CheckModeSkipGenericFunctions) } if yieldExpr.Expression() != nil && c.isConstContext(yieldExpr.Expression()) { yieldExprType = c.getRegularTypeOfLiteralType(yieldExprType) @@ -19868,6 +19868,7 @@ func (c *Checker) checkAndAggregateYieldOperandTypes(fn *ast.Node, checkMode Che if nextType != nil { nextTypes = core.AppendIfUnique(nextTypes, nextType) } + return false }) return yieldTypes, nextTypes } @@ -29953,7 +29954,10 @@ func (c *Checker) instantiateContextualType(contextualType *Type, node *ast.Node if contextFlags&ContextFlagsSignature != 0 && core.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 c.instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper) + t := c.instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper) + if t.flags&TypeFlagsAnyOrUnknown == 0 { + return t + } } if inferenceContext.returnMapper != nil { // For other purposes (e.g. determining whether to produce literal types) we only @@ -29961,12 +29965,14 @@ func (c *Checker) instantiateContextualType(contextualType *Type, node *ast.Node // the 'boolean' type from the contextual type such that contextually typed boolean // literals actually end up widening to 'boolean' (see #48363). t := c.instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper) - if t.flags&TypeFlagsUnion != 0 && containsType(t.Types(), c.regularFalseType) && containsType(t.Types(), c.regularTrueType) { - return c.filterType(t, func(t *Type) bool { - return t != c.regularFalseType && t != c.regularTrueType - }) + if t.flags&TypeFlagsAnyOrUnknown == 0 { + if t.flags&TypeFlagsUnion != 0 && containsType(t.Types(), c.regularFalseType) && containsType(t.Types(), c.regularTrueType) { + return c.filterType(t, func(t *Type) bool { + return t != c.regularFalseType && t != c.regularTrueType + }) + } + return t } - return t } } } @@ -30045,12 +30051,15 @@ func (c *Checker) isContextSensitive(node *ast.Node) bool { // It is possible to that node.expression is undefined (e.g
) expression := node.Expression() return expression != nil && c.isContextSensitive(expression) + case ast.KindYieldExpression: + expression := node.Expression() + return expression != nil && c.isContextSensitive(expression) } return false } func (c *Checker) isContextSensitiveFunctionLikeDeclaration(node *ast.Node) bool { - return ast.HasContextSensitiveParameters(node) || c.hasContextSensitiveReturnExpression(node) + return ast.HasContextSensitiveParameters(node) || c.hasContextSensitiveReturnExpression(node) || c.hasContextSensitiveYieldExpression(node) } func (c *Checker) hasContextSensitiveReturnExpression(node *ast.Node) bool { @@ -30069,6 +30078,10 @@ func (c *Checker) hasContextSensitiveReturnExpression(node *ast.Node) bool { }) } +func (c *Checker) hasContextSensitiveYieldExpression(node *ast.Node) bool { + return getFunctionFlags(node)&FunctionFlagsGenerator != 0 && node.Body() != nil && forEachYieldExpression(node.Body(), c.isContextSensitive) +} + func (c *Checker) pushInferenceContext(node *ast.Node, context *InferenceContext) { c.inferenceContextInfos = append(c.inferenceContextInfos, InferenceContextInfo{node, context}) } diff --git a/internal/checker/utilities.go b/internal/checker/utilities.go index 41c4901e661..873b1f76d23 100644 --- a/internal/checker/utilities.go +++ b/internal/checker/utilities.go @@ -1195,16 +1195,19 @@ func getSuperContainer(node *ast.Node, stopOnFunctions bool) *ast.Node { } } -func forEachYieldExpression(body *ast.Node, visitor func(expr *ast.Node)) { +func forEachYieldExpression(body *ast.Node, visitor func(expr *ast.Node) bool) bool { var traverse func(*ast.Node) bool traverse = func(node *ast.Node) bool { switch node.Kind { case ast.KindYieldExpression: - visitor(node) + if visitor(node) { + return true + } operand := node.Expression() - if operand != nil { - traverse(operand) + if operand == nil { + return false } + return traverse(operand) case ast.KindEnumDeclaration, ast.KindInterfaceDeclaration, ast.KindModuleDeclaration, ast.KindTypeAliasDeclaration: // These are not allowed inside a generator now, but eventually they may be allowed // as local types. Regardless, skip them to avoid the work. @@ -1213,17 +1216,17 @@ func forEachYieldExpression(body *ast.Node, visitor func(expr *ast.Node)) { if node.Name() != nil && ast.IsComputedPropertyName(node.Name()) { // Note that we will not include methods/accessors of a class because they would require // first descending into the class. This is by design. - traverse(node.Name().Expression()) + return traverse(node.Name().Expression()) } } else if !ast.IsPartOfTypeNode(node) { // This is the general case, which should include mostly expressions and statements. // Also includes NodeArrays. - node.ForEachChild(traverse) + return node.ForEachChild(traverse) } } return false } - traverse(body) + return traverse(body) } func getEnclosingContainer(node *ast.Node) *ast.Node { diff --git a/testdata/baselines/reference/submodule/compiler/circularlySimplifyingConditionalTypesNoCrash.types b/testdata/baselines/reference/submodule/compiler/circularlySimplifyingConditionalTypesNoCrash.types index 69f8bc39df0..8179ec3240b 100644 --- a/testdata/baselines/reference/submodule/compiler/circularlySimplifyingConditionalTypesNoCrash.types +++ b/testdata/baselines/reference/submodule/compiler/circularlySimplifyingConditionalTypesNoCrash.types @@ -56,7 +56,7 @@ declare var connect: Connect; const myStoreConnect: Connect = function( >myStoreConnect : Connect ->function( mapStateToProps?: any, mapDispatchToProps?: any, mergeProps?: any, options: unknown = {},) { return connect( mapStateToProps, mapDispatchToProps, mergeProps, options, );} :