diff --git a/src/compiler/symbolWalker.ts b/src/compiler/symbolWalker.ts
index 3f1181236f90a..d09c8bd8878dd 100644
--- a/src/compiler/symbolWalker.ts
+++ b/src/compiler/symbolWalker.ts
@@ -45,6 +45,9 @@ export function createGetSymbolWalker(
const visitedTypes: Type[] = []; // Sparse array from id to type
const visitedSymbols: Symbol[] = []; // Sparse array from id to symbol
+ const maxRecursionDepth = 100;
+ let level = 0;
+
return {
walkType: type => {
try {
@@ -76,41 +79,57 @@ export function createGetSymbolWalker(
if (visitedTypes[type.id]) {
return;
}
- visitedTypes[type.id] = type;
-
- // Reuse visitSymbol to visit the type's symbol,
- // but be sure to bail on recuring into the type if accept declines the symbol.
- const shouldBail = visitSymbol(type.symbol);
- if (shouldBail) return;
-
- // Visit the type's related types, if any
- if (type.flags & TypeFlags.Object) {
- const objectType = type as ObjectType;
- const objectFlags = objectType.objectFlags;
- if (objectFlags & ObjectFlags.Reference) {
- visitTypeReference(type as TypeReference);
+
+ // Prevent infinite recursion by limiting depth
+ if (level >= maxRecursionDepth) {
+ return;
+ }
+
+ // Increment recursion level
+ level++;
+
+ // Wrap logic in try-finally to ensure level is decremented even on early return or error
+ try {
+ visitedTypes[type.id] = type;
+
+ // Reuse visitSymbol to visit the type's symbol,
+ // but be sure to bail on recuring into the type if accept declines the symbol.
+ const shouldBail = visitSymbol(type.symbol);
+ if (shouldBail) return;
+
+ // Visit the type's related types, if any
+ if (type.flags & TypeFlags.Object) {
+ const objectType = type as ObjectType;
+ const objectFlags = objectType.objectFlags;
+ if (objectFlags & ObjectFlags.Reference) {
+ visitTypeReference(type as TypeReference);
+ }
+ if (objectFlags & ObjectFlags.Mapped) {
+ visitMappedType(type as MappedType);
+ }
+ if (objectFlags & (ObjectFlags.Class | ObjectFlags.Interface)) {
+ visitInterfaceType(type as InterfaceType);
+ }
+ if (objectFlags & (ObjectFlags.Tuple | ObjectFlags.Anonymous)) {
+ visitObjectType(objectType);
+ }
}
- if (objectFlags & ObjectFlags.Mapped) {
- visitMappedType(type as MappedType);
+ if (type.flags & TypeFlags.TypeParameter) {
+ visitTypeParameter(type as TypeParameter);
}
- if (objectFlags & (ObjectFlags.Class | ObjectFlags.Interface)) {
- visitInterfaceType(type as InterfaceType);
+ if (type.flags & TypeFlags.UnionOrIntersection) {
+ visitUnionOrIntersectionType(type as UnionOrIntersectionType);
}
- if (objectFlags & (ObjectFlags.Tuple | ObjectFlags.Anonymous)) {
- visitObjectType(objectType);
+ if (type.flags & TypeFlags.Index) {
+ visitIndexType(type as IndexType);
+ }
+ if (type.flags & TypeFlags.IndexedAccess) {
+ visitIndexedAccessType(type as IndexedAccessType);
}
}
- if (type.flags & TypeFlags.TypeParameter) {
- visitTypeParameter(type as TypeParameter);
- }
- if (type.flags & TypeFlags.UnionOrIntersection) {
- visitUnionOrIntersectionType(type as UnionOrIntersectionType);
- }
- if (type.flags & TypeFlags.Index) {
- visitIndexType(type as IndexType);
- }
- if (type.flags & TypeFlags.IndexedAccess) {
- visitIndexedAccessType(type as IndexedAccessType);
+ finally {
+ // Decrement recursion level when exiting
+ level--;
}
}
diff --git a/tests/cases/fourslash/extractFunctionSymbolWalkerInfiniteLoop.ts b/tests/cases/fourslash/extractFunctionSymbolWalkerInfiniteLoop.ts
new file mode 100644
index 0000000000000..17156b4bc4594
--- /dev/null
+++ b/tests/cases/fourslash/extractFunctionSymbolWalkerInfiniteLoop.ts
@@ -0,0 +1,16 @@
+///
+// @lib: esnext,dom
+// @strict: true
+
+////function func(param: ProblematicType) {}
+////type ProblematicType = {
+//// prop: ProblematicType;
+////};
+////class TestRefactoring {
+//// createElement() {
+//// [|document.createElement('span');|]
+//// }
+////}
+
+goTo.selectRange(test.ranges()[0]);
+verify.not.refactorAvailable("Extract function");
\ No newline at end of file