Skip to content

Commit

Permalink
Fixed a bug that results in a hang under certain circumstances when u…
Browse files Browse the repository at this point in the history
…sing a recursive type alias. This addresses #9497.
  • Loading branch information
erictraut committed Nov 29, 2024
1 parent 4042841 commit abf178b
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 5 deletions.
13 changes: 8 additions & 5 deletions packages/pyright-internal/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7717,15 +7717,18 @@ export function createTypeEvaluator(
}

const transformedType = transformPossibleRecursiveTypeAlias(type);
const isRecursiveTypeAlias = transformedType !== type;

// If this is a recursive type alias, see if we've already recursed
// seen it once before in the recursion stack. If so, don't recurse
// further.
if (transformedType !== type) {
if (isRecursiveTypeAlias) {
const pendingOverlaps = pendingTypes.filter((pendingType) => isTypeSame(pendingType, type));
if (pendingOverlaps.length > 1) {
return;
}

pendingTypes.push(type);
}

recursionCount++;
Expand All @@ -7737,8 +7740,6 @@ export function createTypeEvaluator(
if (typeParamIndex >= 0) {
usageVariances[typeParamIndex] = combineVariances(usageVariances[typeParamIndex], variance);
} else {
pendingTypes.push(type);

updateUsageVariancesRecursive(
subtype,
typeAliasTypeParams,
Expand All @@ -7747,8 +7748,6 @@ export function createTypeEvaluator(
pendingTypes,
recursionCount
);

pendingTypes.pop();
}
});
}
Expand Down Expand Up @@ -7796,6 +7795,10 @@ export function createTypeEvaluator(
}
}
});

if (isRecursiveTypeAlias) {
pendingTypes.pop();
}
}

function getIndexAccessMagicMethodName(usage: EvaluatorUsage): string {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# This sample tests a case that previously resulted in infinite recursion.

from typing import TypeVar, Generic

U = TypeVar("U")
T = TypeVar("T")


class A(Generic[T]):
pass


class B(Generic[T]):
pass


class C(Generic[T]):
pass


TA1 = A["TA2[U]"] | B["TA2[U]"]
TA2 = TA1[U] | C[TA1[U]]
TA3 = TA2[U]
9 changes: 9 additions & 0 deletions packages/pyright-internal/src/tests/typeEvaluator3.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,15 @@ test('RecursiveTypeAlias15', () => {
TestUtils.validateResults(analysisResults, 4);
});

test('RecursiveTypeAlias16', () => {
const configOptions = new ConfigOptions(Uri.empty());

configOptions.defaultPythonVersion = pythonVersion3_12;
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['recursiveTypeAlias16.py'], configOptions);

TestUtils.validateResults(analysisResults, 0);
});

test('Classes1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['classes1.py']);

Expand Down

0 comments on commit abf178b

Please sign in to comment.