Skip to content

Fix infinite loop in symbolWalker during extract function refactoring#62907

Closed
Dino0204 wants to merge 6 commits intomicrosoft:mainfrom
Dino0204:main
Closed

Fix infinite loop in symbolWalker during extract function refactoring#62907
Dino0204 wants to merge 6 commits intomicrosoft:mainfrom
Dino0204:main

Conversation

@Dino0204
Copy link
Copy Markdown

Fixes #62471

Description

This PR fixes a RangeError: Maximum call stack size exceeded crash in the TypeScript Server (VS Code Extension) that occurs when invoking "Refactor..." actions on code involving recursive generic types.

The issue was caused by symbolWalker entering an infinite loop when traversing recursive types (e.g., a type referencing itself in a property). While the walker already had cycle detection via visitedTypes and visitedSymbols, it lacked a depth limit. This allowed cases where the recursion depth exceeded the JavaScript call stack limit before the cycle detection could take effect.

Reproduction

The following code triggers the crash when trying to use "Refactor..." (e.g., Extract Function) on the selection:

type ProblematicType<T, D> = {
  prop: ProblematicType<T[keyof T], any>;
};

function func<T, D>(param: ProblematicType<T, D>) {}

class TestRefactoring<T> {
  method() {
    func<T, any>(null as any);
  }
}

Behavior:

  • Before: TS Server crashes immediately with RangeError: Maximum call stack size exceeded.*
스크린샷 2025-12-18 112531
  • After: Refactoring menu opens correctly (or shows "No refactoring available"), and no crash occurs.*
스크린샷 2025-12-18 112200

Implementation Details

I modified src/compiler/symbolWalker.ts to add recursion depth tracking in the visitType function.

Key Changes:

  1. Depth Tracking: Introduced a level counter and a maxRecursionDepth constant (set to 100) to track the current recursion depth.
  2. Early Termination: Added a check to return early if level > maxRecursionDepth, preventing the stack overflow.
  3. Proper Cleanup: Wrapped the type visiting logic in a try-finally block to ensure the level counter is properly decremented even on early returns or errors.

Why this approach (Defense-in-Depth)?
This works in conjunction with the existing cycle detection (visitedTypes) to provide a robust safeguard:

  • The visitedTypes array prevents infinite loops on direct cycles.
  • The new depth limit prevents stack overflow on deeply nested recursive structures that might blow the stack before completing a full cycle.

Test Plan

  1. New Test Case: Added a new fourslash test case tests/cases/fourslash/extractFunctionSymbolWalkerInfiniteLoop.ts.
  • This test reproduces the crash scenario by invoking verify.not.refactorAvailable("Extract function") on the recursive type pattern shown above.
  • The test verifies that the refactoring is correctly unavailable (or available without crashing) for this edge case.
  • The test now passes without causing a stack overflow.
  1. Regression Testing:
  • Ran existing tests to ensure no regressions in other refactoring features.
  • Successfully ran hereby runtests locally.

Baseline Changes

  • None: This change only fixes the crash logic and does not alter emitted output or existing error messages. The change is purely defensive and only affects the internal traversal mechanism of the symbolWalker.

Copilot AI review requested due to automatic review settings December 18, 2025 02:31
@github-project-automation github-project-automation bot moved this to Not started in PR Backlog Dec 18, 2025
@typescript-bot typescript-bot added the For Backlog Bug PRs that fix a backlog bug label Dec 18, 2025
@Dino0204
Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a stack overflow crash in the TypeScript language server that occurs during refactoring operations on code with deeply recursive generic types. The fix adds a recursion depth limit to the symbolWalker traversal logic to complement the existing cycle detection mechanism.

Key changes:

  • Added depth tracking (level counter and maxRecursionDepth constant) to visitType function in symbolWalker
  • Wrapped type visiting logic in try-finally to ensure proper depth counter cleanup
  • Added a fourslash test to verify the fix prevents crashes

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/compiler/symbolWalker.ts Adds recursion depth tracking with a limit of 100 levels to prevent stack overflow when traversing deeply nested recursive types
tests/cases/fourslash/extractFunctionSymbolWalkerInfiniteLoop.ts Adds regression test for the symbolWalker infinite loop fix

@github-project-automation github-project-automation bot moved this from Not started to Done in PR Backlog Mar 24, 2026
@typescript-bot
Copy link
Copy Markdown
Collaborator

With 6.0 out as the final release vehicle for this codebase, we're closing all PRs that don't fit the merge criteria for post-6.0 patches. If you think this was a mistake and this PR fits the post-6.0 patch criteria, please post to the 6.0 iteration issue with details (specifically, which PR and which patch criteria it satisfies).

Next steps for PRs:

  • For crash bugfixes or language service improvements, PRs are currently accepted at the typescript-go repo
  • Changes to type system behavior should wait until after 7.0, at which point mainline TypeScript development will resume in this repository with the Go codebase
  • Library file updates (lib.d.ts etc) continue to live in this repo or the DOM Generator repo as appropriate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

For Backlog Bug PRs that fix a backlog bug

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Certain recursive types cause "Maximum call stack size exceeded" error in TS server

3 participants