diff --git a/packages/pyright-internal/src/analyzer/typeUtils.ts b/packages/pyright-internal/src/analyzer/typeUtils.ts index 2e25d72ae320..e15de57660e6 100644 --- a/packages/pyright-internal/src/analyzer/typeUtils.ts +++ b/packages/pyright-internal/src/analyzer/typeUtils.ts @@ -222,6 +222,13 @@ export interface AddConditionOptions { skipBoundTypeVars?: boolean; } +// There are cases where tuple types can be infinitely nested. The +// recursion count limit will eventually be hit, but this will create +// deep types that will effectively hang the analyzer. To prevent this, +// we'll limit the depth of the tuple type arguments. This value is +// large enough that we should never hit it in legitimate circumstances. +const maxTupleTypeArgRecursionDepth = 10; + // Tracks whether a function signature has been seen before within // an expression. For example, in the expression "foo(foo, foo)", the // signature for "foo" will be seen three times at three different @@ -3654,6 +3661,10 @@ export class TypeVarTransformer { // Handle tuples specially. if (ClassType.isTupleClass(classType)) { + if (getContainerDepth(classType) > maxTupleTypeArgRecursionDepth) { + return classType; + } + if (classType.priv.tupleTypeArgs) { newTupleTypeArgs = []; diff --git a/packages/pyright-internal/src/tests/samples/tuple19.py b/packages/pyright-internal/src/tests/samples/tuple19.py new file mode 100644 index 000000000000..761c487a444a --- /dev/null +++ b/packages/pyright-internal/src/tests/samples/tuple19.py @@ -0,0 +1,11 @@ +# This sample tests a case where the constraint solver generates a very +# "deep" tuple type. Previously, this caused a hang in the evaluator. + +from typing import Callable + + +def func1[T](c: Callable[[T], T]): ... + + +# This should generate an error, not hang. +func1(lambda v: (v, v)) diff --git a/packages/pyright-internal/src/tests/typeEvaluator8.test.ts b/packages/pyright-internal/src/tests/typeEvaluator8.test.ts index cfbba767a084..40d98d200289 100644 --- a/packages/pyright-internal/src/tests/typeEvaluator8.test.ts +++ b/packages/pyright-internal/src/tests/typeEvaluator8.test.ts @@ -539,6 +539,12 @@ test('Tuple18', () => { TestUtils.validateResults(analysisResults, 2); }); +test('Tuple19', () => { + const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tuple19.py']); + + TestUtils.validateResults(analysisResults, 1); +}); + test('NamedTuple1', () => { const analysisResults = TestUtils.typeAnalyzeSampleFiles(['namedTuple1.py']);