fix(checker): avoid stack overflow for self-referential computed enum member names#63567
Open
yen0304 wants to merge 1 commit into
Open
fix(checker): avoid stack overflow for self-referential computed enum member names#63567yen0304 wants to merge 1 commit into
yen0304 wants to merge 1 commit into
Conversation
… member names
A computed enum member name that refers back to a member of the same enum
(e.g. `enum E { [object] = 1, object = 2 }`) caused the checker to recurse until
the stack overflowed: getDeclaredTypeOfEnum determines whether each member name
is late-bindable, which resolves the referenced member's type, which re-enters
getDeclaredTypeOfEnum for the same enum.
Track the enums whose member names are currently being late-bound and skip
late-binding of computed names while that resolution is in progress. Computed
names are not permitted on enum members anyway (TS1164 is still reported), so
this only affects already-invalid code, and the non-recursive paths are
unchanged (no baseline updates outside the new regression test).
Fixes microsoft#63173
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a checker stack overflow triggered by self-referential computed enum member names (invalid code, but should not crash). The change adds a re-entrancy guard during enum declared-type construction and introduces a compiler regression test covering const enum (ambient + non-ambient) and regular enum cases.
Changes:
- Add
enumsResolvingLateBoundNamestracking to prevent recursive late-binding of computed enum member names from re-enteringgetDeclaredTypeOfEnumindefinitely. - Adjust enum declared-type caching to avoid overwriting a declared type that may have been established by a re-entrant call.
- Add a compiler test + baselines to ensure the scenario reports TS1164 and does not crash.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/compiler/checker.ts | Adds a recursion guard when determining enum member name bindability during enum type construction. |
| tests/cases/compiler/enumComputedNameSelfReferenceCrash.ts | New regression test reproducing the prior stack overflow and validating non-crash behavior. |
| tests/baselines/reference/enumComputedNameSelfReferenceCrash.errors.txt | Baseline for expected TS1164 diagnostics. |
| tests/baselines/reference/enumComputedNameSelfReferenceCrash.js | Baseline for JS/declaration emit under error conditions. |
| tests/baselines/reference/enumComputedNameSelfReferenceCrash.symbols | Baseline for symbol binding expectations in the test. |
| tests/baselines/reference/enumComputedNameSelfReferenceCrash.types | Baseline for type display expectations in the test. |
Comment on lines
13563
to
+13567
| for (const declaration of symbol.declarations) { | ||
| if (declaration.kind === SyntaxKind.EnumDeclaration) { | ||
| for (const member of (declaration as EnumDeclaration).members) { | ||
| if (hasBindableName(member)) { | ||
| const bindable = resolvingLateBoundNames ? !hasDynamicName(member) : hasBindableName(member); | ||
| if (bindable) { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #63173
Problem
A computed enum member name that refers back to a member of the same enum crashes the checker with
RangeError: Maximum call stack size exceeded:Root cause
getDeclaredTypeOfEnumiterates the members and callshasBindableName(member)for each. For the computed name[object], that goes throughhasLateBindableName→checkComputedPropertyName, which resolves the type of the referenced memberobject. Resolving an enum member's type callsgetDeclaredTypeOfEnumMember→getDeclaredTypeOfEnumfor the same enum, whose declared type is not set until the member loop finishes — so determining[object]'s bindability requires the enum type, which requires[object]'s bindability, and so on until the stack overflows.Observed stack:
Fix
Track the enum symbols whose member names are currently being late-bound (
enumsResolvingLateBoundNames). While an enum is in that set, computed (dynamic) member names are treated as non-bindable rather than being late-bound again, which breaks the cycle.This only changes behavior for the recursive case:
TS1164is still reported, so this path is always already an error.enumBasics2/enumBasics3tests) goes through initializer evaluation, not name late-binding, so it is unaffected. Only the late-binding determination is guarded.After the fix the example no longer crashes and reports the expected error:
Tests
tests/cases/compiler/enumComputedNameSelfReferenceCrash.tscoveringconst enum(ambient and non-ambient) and a regularenum, with both bare-identifier ([object]) and qualified ([F.A]) computed names.hereby runtests— full suite passes (106373 passing, 0 failing); no baseline changes outside the new test.hereby lintclean on the changed file.