From 2aa9fe47099236aca2c9919085bc67915fc40fa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 8 May 2023 22:12:59 +0200 Subject: [PATCH] Only collect outermost intra expression inference sites --- src/compiler/checker.ts | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 98cd87923761e..74444e00bd15d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1255,9 +1255,10 @@ export const enum CheckMode { Inferential = 1 << 1, // Inferential typing SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions SkipGenericFunctions = 1 << 3, // Skip single signature generic functions - IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help - IsForStringLiteralArgumentCompletions = 1 << 5, // Do not infer from the argument currently being typed - RestBindingElement = 1 << 6, // Checking a type that is going to be used to determine the type of a rest binding element + SkipAddingIntraExpressionSites = 1 << 4, // Skip adding intra expression sites in nested expressions since only the outermost one has to be added + IsForSignatureHelp = 1 << 5, // Call resolution for purposes of signature help + IsForStringLiteralArgumentCompletions = 1 << 6, // Do not infer from the argument currently being typed + RestBindingElement = 1 << 7, // Checking a type that is going to be used to determine the type of a rest binding element // e.g. in `const { a, ...rest } = foo`, when checking the type of `foo` to determine the type of `rest`, // we need to preserve generic types instead of substituting them for constraints } @@ -29980,10 +29981,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { elementFlags.push(ElementFlags.Optional); } else { - const type = checkExpressionForMutableLocation(e, checkMode, forceTuple); + const shouldAddAsIntraExpressionInferenceSite = inTupleContext && checkMode && checkMode & CheckMode.Inferential && !(checkMode & (CheckMode.SkipContextSensitive | CheckMode.SkipAddingIntraExpressionSites)) && isContextSensitive(e); + const elementCheckMode = (checkMode || CheckMode.Normal) | (shouldAddAsIntraExpressionInferenceSite ? CheckMode.SkipAddingIntraExpressionSites : 0); + + const type = checkExpressionForMutableLocation(e, elementCheckMode, forceTuple); elementTypes.push(addOptionality(type, /*isProperty*/ true, hasOmittedExpression)); elementFlags.push(hasOmittedExpression ? ElementFlags.Optional : ElementFlags.Required); - if (inTupleContext && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && isContextSensitive(e)) { + if (shouldAddAsIntraExpressionInferenceSite) { const inferenceContext = getInferenceContext(node); Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context addIntraExpressionInferenceSite(inferenceContext, e, type); @@ -30106,7 +30110,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return links.immediateTarget; } - function checkObjectLiteral(node: ObjectLiteralExpression, checkMode?: CheckMode): Type { + function checkObjectLiteral(node: ObjectLiteralExpression, checkMode = CheckMode.Normal): Type { const inDestructuringPattern = isAssignmentTarget(node); // Grammar checking checkGrammarObjectLiteralExpression(node, inDestructuringPattern); @@ -30148,12 +30152,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (memberDecl.kind === SyntaxKind.PropertyAssignment || memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment || isObjectLiteralMethod(memberDecl)) { - let type = memberDecl.kind === SyntaxKind.PropertyAssignment ? checkPropertyAssignment(memberDecl, checkMode) : + + const shouldAddAsIntraExpressionInferenceSite = contextualType && checkMode & CheckMode.Inferential && !(checkMode & (CheckMode.SkipContextSensitive | CheckMode.SkipAddingIntraExpressionSites)) && + (memberDecl.kind === SyntaxKind.PropertyAssignment || memberDecl.kind === SyntaxKind.MethodDeclaration) && isContextSensitive(memberDecl); + const propCheckMode = checkMode | (shouldAddAsIntraExpressionInferenceSite ? CheckMode.SkipAddingIntraExpressionSites : 0); + + let type = memberDecl.kind === SyntaxKind.PropertyAssignment ? checkPropertyAssignment(memberDecl, propCheckMode) : // avoid resolving the left side of the ShorthandPropertyAssignment outside of the destructuring // for error recovery purposes. For example, if a user wrote `{ a = 100 }` instead of `{ a: 100 }`. // we don't want to say "could not find 'a'". - memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ? checkExpressionForMutableLocation(!inDestructuringPattern && memberDecl.objectAssignmentInitializer ? memberDecl.objectAssignmentInitializer : memberDecl.name, checkMode) : - checkObjectLiteralMethod(memberDecl, checkMode); + memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ? checkExpressionForMutableLocation(!inDestructuringPattern && memberDecl.objectAssignmentInitializer ? memberDecl.objectAssignmentInitializer : memberDecl.name, propCheckMode) : + checkObjectLiteralMethod(memberDecl, propCheckMode); if (isInJavascript) { const jsDocType = getTypeForDeclarationFromJSDocComment(memberDecl); if (jsDocType) { @@ -30208,8 +30217,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { member = prop; allPropertiesTable?.set(prop.escapedName, prop); - if (contextualType && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && - (memberDecl.kind === SyntaxKind.PropertyAssignment || memberDecl.kind === SyntaxKind.MethodDeclaration) && isContextSensitive(memberDecl)) { + if (shouldAddAsIntraExpressionInferenceSite) { const inferenceContext = getInferenceContext(node); Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context const inferenceNode = memberDecl.kind === SyntaxKind.PropertyAssignment ? memberDecl.initializer : memberDecl; @@ -30428,7 +30436,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { * @remarks Because this function calls getSpreadType, it needs to use the same checks as checkObjectLiteral, * which also calls getSpreadType. */ - function createJsxAttributesTypeFromAttributesProperty(openingLikeElement: JsxOpeningLikeElement, checkMode: CheckMode | undefined) { + function createJsxAttributesTypeFromAttributesProperty(openingLikeElement: JsxOpeningLikeElement, checkMode = CheckMode.Normal) { const attributes = openingLikeElement.attributes; const contextualType = getContextualType(attributes, ContextFlags.None); const allAttributesTable = strictNullChecks ? createSymbolTable() : undefined; @@ -30443,7 +30451,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { for (const attributeDecl of attributes.properties) { const member = attributeDecl.symbol; if (isJsxAttribute(attributeDecl)) { - const exprType = checkJsxAttribute(attributeDecl, checkMode); + const shouldAddAsIntraExpressionInferenceSite = contextualType && checkMode & CheckMode.Inferential && !(checkMode & (CheckMode.SkipContextSensitive | CheckMode.SkipAddingIntraExpressionSites)) && isContextSensitive(attributeDecl); + const attributeCheckMode = checkMode | (shouldAddAsIntraExpressionInferenceSite ? CheckMode.SkipAddingIntraExpressionSites : 0); + + const exprType = checkJsxAttribute(attributeDecl, attributeCheckMode); objectFlags |= getObjectFlags(exprType) & ObjectFlags.PropagatingFlags; const attributeSymbol = createSymbol(SymbolFlags.Property | member.flags, member.escapedName); @@ -30465,7 +30476,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { addDeprecatedSuggestion(attributeDecl.name, prop.declarations, attributeDecl.name.escapedText as string); } } - if (contextualType && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && isContextSensitive(attributeDecl)) { + if (shouldAddAsIntraExpressionInferenceSite) { const inferenceContext = getInferenceContext(attributes); Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context const inferenceNode = (attributeDecl.initializer as JsxExpression).expression!;