diff --git a/.gitignore b/.gitignore index 3e7b0fbc6782..0b59e9f51cba 100644 --- a/.gitignore +++ b/.gitignore @@ -131,6 +131,7 @@ Thumbs.db serena/ .beads/ AGENTS.md +/plan # Generated benchmark output packages/pyright-internal/src/tests/benchmarks/.generated/ diff --git a/packages/pyright-internal/src/analyzer/typeEvaluator.ts b/packages/pyright-internal/src/analyzer/typeEvaluator.ts index fef8ab2aac4e..e552fa55b830 100644 --- a/packages/pyright-internal/src/analyzer/typeEvaluator.ts +++ b/packages/pyright-internal/src/analyzer/typeEvaluator.ts @@ -1117,10 +1117,7 @@ export function createTypeEvaluator( if (isAny(anySpecialForm)) { TypeBase.setSpecialForm(anySpecialForm, anyClass); - - if (isTypeFormSupported(node)) { - TypeBase.setTypeForm(anySpecialForm, convertToInstance(anySpecialForm)); - } + TypeBase.setTypeForm(anySpecialForm, convertToInstance(anySpecialForm)); } } } @@ -1314,7 +1311,7 @@ export function createTypeEvaluator( expectingInstantiable = false; } - typeResult = getTypeOfStringList(node, flags); + typeResult = getTypeOfStringList(node, flags, inferenceContext); break; } @@ -1656,7 +1653,11 @@ export function createTypeEvaluator( return typeResult; } - function getTypeOfStringList(node: StringListNode, flags: EvalFlags): TypeResult { + function getTypeOfStringList( + node: StringListNode, + flags: EvalFlags, + inferenceContext?: InferenceContext + ): TypeResult { let typeResult: TypeResult | undefined; if ((flags & EvalFlags.StrLiteralAsType) !== 0 && (flags & EvalFlags.TypeFormArg) === 0) { @@ -1743,11 +1744,15 @@ export function createTypeEvaluator( }; } - if ( - node.d.strings.length !== 1 || - node.d.strings[0].nodeType !== ParseNodeType.String || - !isTypeFormSupported(node) - ) { + // Only attempt to interpret the string as a TypeForm forward reference when + // there's a signal that a TypeForm value is wanted in this context. Doing it + // unconditionally can trigger expensive (and recursion-prone) type lookups + // for plain string literals in non-type contexts. + const wantsTypeForm = + (flags & EvalFlags.TypeFormArg) !== 0 || + (inferenceContext !== undefined && expectedTypeWantsTypeForm(inferenceContext.expectedType)); + + if (node.d.strings.length !== 1 || node.d.strings[0].nodeType !== ParseNodeType.String || !wantsTypeForm) { return typeResult; } @@ -5014,10 +5019,6 @@ export function createTypeEvaluator( } function addTypeFormForSymbol(node: ExpressionNode, type: Type, flags: EvalFlags, includesVarDecl: boolean): Type { - if (!isTypeFormSupported(node)) { - return type; - } - const isValid = isSymbolValidTypeExpression(type, includesVarDecl); // If the type already has type information associated with it, don't replace. @@ -7659,9 +7660,7 @@ export function createTypeEvaluator( typeArgs: aliasTypeArgs, }); - if (isTypeFormSupported(node)) { - type = TypeBase.cloneWithTypeForm(type, reportedError ? undefined : convertToInstance(type)); - } + type = TypeBase.cloneWithTypeForm(type, reportedError ? undefined : convertToInstance(type)); if (baseType.props?.typeAliasInfo) { return { type, node }; @@ -8832,7 +8831,7 @@ export function createTypeEvaluator( } const typeFormResult = getTypeOfArgExpectingType(convertNodeToArg(node.d.args[0]), { - typeFormArg: isTypeFormSupported(node), + typeFormArg: true, noNonTypeSpecialForms: true, typeExpression: true, }); @@ -13852,9 +13851,7 @@ export function createTypeEvaluator( ? prefetched.noneTypeClass : convertToInstance(prefetched.noneTypeClass); - if (isTypeFormSupported(node)) { - type = TypeBase.cloneWithTypeForm(type, convertToInstance(type)); - } + type = TypeBase.cloneWithTypeForm(type, convertToInstance(type)); } } else if ( node.d.constType === KeywordType.True || @@ -15738,7 +15735,7 @@ export function createTypeEvaluator( FunctionType.addParamSpecVariadics(functionType, convertToInstance(paramSpec)); } - if (isTypeFormSupported(errorNode) && isValidTypeForm) { + if (isValidTypeForm) { functionType = TypeBase.cloneWithTypeForm(functionType, convertToInstance(functionType)); } @@ -15954,7 +15951,7 @@ export function createTypeEvaluator( result = TypeBase.cloneAsSpecialForm(result, ClassType.cloneAsInstance(prefetched.unionTypeClass)); } - if (isTypeFormSupported(node) && isValidTypeForm) { + if (isValidTypeForm) { result = TypeBase.cloneWithTypeForm(result, convertToInstance(result)); } @@ -16025,9 +16022,7 @@ export function createTypeEvaluator( }); let resultType = ClassType.specialize(classType, convertedTypeArgs); - if (isTypeFormSupported(errorNode)) { - resultType = TypeBase.cloneWithTypeForm(resultType, convertToInstance(resultType)); - } + resultType = TypeBase.cloneWithTypeForm(resultType, convertToInstance(resultType)); return resultType; } @@ -16059,9 +16054,7 @@ export function createTypeEvaluator( let resultType = ClassType.specialize(classType, convertedTypeArgs); - if (isTypeFormSupported(errorNode)) { - resultType = TypeBase.cloneWithTypeForm(resultType, convertToInstance(resultType)); - } + resultType = TypeBase.cloneWithTypeForm(resultType, convertToInstance(resultType)); return resultType; } @@ -16664,7 +16657,7 @@ export function createTypeEvaluator( if (unionType.props?.typeForm) { unionType = TypeBase.cloneWithTypeForm(unionType, undefined); } - } else if (isTypeFormSupported(errorNode)) { + } else { const typeFormType = combineTypes(types.map((t) => t.props!.typeForm!)); unionType = TypeBase.cloneWithTypeForm(unionType, typeFormType); } @@ -17025,9 +17018,7 @@ export function createTypeEvaluator( specialType.shared.baseClasses.push(prefetched?.strClass ?? AnyType.create()); computeMroLinearization(specialType); - if (isTypeFormSupported(node)) { - specialType = TypeBase.cloneWithTypeForm(specialType, convertToInstance(specialType)); - } + specialType = TypeBase.cloneWithTypeForm(specialType, convertToInstance(specialType)); } // Handle 'Never' and 'NoReturn' specially. @@ -17037,9 +17028,7 @@ export function createTypeEvaluator( specialType ); - if (isTypeFormSupported(node)) { - specialType = TypeBase.cloneWithTypeForm(specialType, convertToInstance(specialType)); - } + specialType = TypeBase.cloneWithTypeForm(specialType, convertToInstance(specialType)); } writeTypeCache(node, { type: specialType }, EvalFlags.None); @@ -21244,9 +21233,7 @@ export function createTypeEvaluator( let resultType = aliasedName === 'Never' ? NeverType.createNever() : NeverType.createNoReturn(); resultType = TypeBase.cloneAsSpecialForm(resultType, classType); - if (isTypeFormSupported(errorNode)) { - resultType = TypeBase.cloneWithTypeForm(resultType, convertToInstance(resultType)); - } + resultType = TypeBase.cloneWithTypeForm(resultType, convertToInstance(resultType)); return { type: resultType }; } @@ -21268,9 +21255,7 @@ export function createTypeEvaluator( typeType = explodeGenericClass(typeType); } - if (isTypeFormSupported(errorNode)) { - typeType = TypeBase.cloneWithTypeForm(typeType, convertToInstance(typeType)); - } + typeType = TypeBase.cloneWithTypeForm(typeType, convertToInstance(typeType)); return { type: typeType }; } @@ -21430,9 +21415,7 @@ export function createTypeEvaluator( typeType = explodeGenericClass(typeType); } - if (isTypeFormSupported(errorNode)) { - typeType = TypeBase.cloneWithTypeForm(typeType, convertToInstance(typeType)); - } + typeType = TypeBase.cloneWithTypeForm(typeType, convertToInstance(typeType)); return { type: typeType }; } @@ -21449,12 +21432,7 @@ export function createTypeEvaluator( /* isSpecialForm */ false ); - if (isTypeFormSupported(errorNode)) { - specializedClass = TypeBase.cloneWithTypeForm( - specializedClass, - convertToInstance(specializedClass) - ); - } + specializedClass = TypeBase.cloneWithTypeForm(specializedClass, convertToInstance(specializedClass)); return { type: specializedClass }; } @@ -21715,12 +21693,10 @@ export function createTypeEvaluator( let specializedClass = ClassType.specialize(classType, typeArgTypes, typeArgs !== undefined); - if (isTypeFormSupported(errorNode)) { - specializedClass = TypeBase.cloneWithTypeForm( - specializedClass, - isValidTypeForm ? convertToInstance(specializedClass) : undefined - ); - } + specializedClass = TypeBase.cloneWithTypeForm( + specializedClass, + isValidTypeForm ? convertToInstance(specializedClass) : undefined + ); return { type: specializedClass }; } @@ -25598,6 +25574,16 @@ export function createTypeEvaluator( return isAssignable; } + function expectedTypeWantsTypeForm(expectedType: Type): boolean { + let result = false; + doForEachSubtype(expectedType, (subtype) => { + if (isClassInstance(subtype) && ClassType.isBuiltIn(subtype, 'TypeForm')) { + result = true; + } + }); + return result; + } + // If the expected type is an explicit TypeForm type, see if the source // type has an implicit TypeForm type that can be assigned to it. If so, // convert to an explicit TypeForm type. @@ -28701,13 +28687,6 @@ export function createTypeEvaluator( return { sourceType: simpleSrcType, destType: simpleDestType }; } - function isTypeFormSupported(node: ParseNode) { - const fileInfo = AnalyzerNodeInfo.getFileInfo(node); - - // For now, enable only if enableExperimentalFeatures is true. - return fileInfo.diagnosticRuleSet.enableExperimentalFeatures; - } - function printType(type: Type, options?: PrintTypeOptions): string { let flags = evaluatorOptions.printTypeFlags;