diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0567712f11da3..8fb83983e8b06 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8661,6 +8661,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function symbolToTypeNode(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, overrideTypeArguments?: readonly TypeNode[]): TypeNode { + // Prevent infinite recursion when a function's return type references itself (e.g., ReturnType) + if (!context.visitedSymbols) { + context.visitedSymbols = new Set(); + } + const symbolKey = `${getSymbolId(symbol)}|${meaning}`; + if (context.visitedSymbols.has(symbolKey)) { + // Detected recursive symbol reference, return never type to avoid crash + return factory.createKeywordTypeNode(SyntaxKind.NeverKeyword); + } + context.visitedSymbols.add(symbolKey); + const chain = lookupSymbolChain(symbol, context, meaning, !(context.flags & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope)); // If we're using aliases outside the current scope, dont bother with the module const isTypeOf = meaning === SymbolFlags.Value; @@ -8723,33 +8734,39 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const lit = factory.createLiteralTypeNode(factory.createStringLiteral(specifier)); context.approximateLength += specifier.length + 10; // specifier + import("") + let result: TypeNode; if (!nonRootParts || isEntityName(nonRootParts)) { if (nonRootParts) { const lastId = isIdentifier(nonRootParts) ? nonRootParts : nonRootParts.right; setIdentifierTypeArguments(lastId, /*typeArguments*/ undefined); } - return factory.createImportTypeNode(lit, attributes, nonRootParts as EntityName, typeParameterNodes as readonly TypeNode[], isTypeOf); + result = factory.createImportTypeNode(lit, attributes, nonRootParts as EntityName, typeParameterNodes as readonly TypeNode[], isTypeOf); } else { const splitNode = getTopmostIndexedAccessType(nonRootParts); const qualifier = (splitNode.objectType as TypeReferenceNode).typeName; - return factory.createIndexedAccessTypeNode(factory.createImportTypeNode(lit, attributes, qualifier, typeParameterNodes as readonly TypeNode[], isTypeOf), splitNode.indexType); + result = factory.createIndexedAccessTypeNode(factory.createImportTypeNode(lit, attributes, qualifier, typeParameterNodes as readonly TypeNode[], isTypeOf), splitNode.indexType); } + context.visitedSymbols.delete(symbolKey); + return result; } const entityName = createAccessFromSymbolChain(chain, chain.length - 1, 0); + let result: TypeNode; if (isIndexedAccessTypeNode(entityName)) { - return entityName; // Indexed accesses can never be `typeof` + result = entityName; // Indexed accesses can never be `typeof` } - if (isTypeOf) { - return factory.createTypeQueryNode(entityName); + else if (isTypeOf) { + result = factory.createTypeQueryNode(entityName); } else { const lastId = isIdentifier(entityName) ? entityName : entityName.right; const lastTypeArgs = getIdentifierTypeArguments(lastId); setIdentifierTypeArguments(lastId, /*typeArguments*/ undefined); - return factory.createTypeReferenceNode(entityName, lastTypeArgs as NodeArray); + result = factory.createTypeReferenceNode(entityName, lastTypeArgs as NodeArray); } + context.visitedSymbols.delete(symbolKey); + return result; function createAccessFromSymbolChain(chain: Symbol[], index: number, stopper: number): EntityName | IndexedAccessTypeNode { const typeParameterNodes = index === (chain.length - 1) ? overrideTypeArguments : lookupTypeParameterNodes(chain, index, context); @@ -53030,7 +53047,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { let current: Node = node; while (current) { if (isFunctionLikeOrClassStaticBlockDeclaration(current)) { - return grammarErrorOnNode(node, Diagnostics.Jump_target_cannot_cross_function_boundary); + return grammarErrorOnNode(node, Diagnostics.Jump_target_0_cannot_cross_function_boundary, node.label ? node.label.text : ""); } switch (current.kind) { @@ -54300,6 +54317,7 @@ interface NodeBuilderContext extends SyntacticTypeNodeBuilderContext { reportedDiagnostic: boolean; trackedSymbols: TrackedSymbol[] | undefined; visitedTypes: Set | undefined; + visitedSymbols: Set | undefined; symbolDepth: Map | undefined; inferTypeParameters: TypeParameter[] | undefined; approximateLength: number; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 46c280799d7f9..a7ee23c49b69d 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -307,6 +307,10 @@ "category": "Error", "code": 1107 }, + "Jump target '{0}' cannot cross function boundary.": { + "category": "Error", + "code": 1107 + }, "A 'return' statement can only be used within a function body.": { "category": "Error", "code": 1108