From f25ad4737b1081bbe677e87ffe21c19ea7c196f9 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Sun, 10 Nov 2024 12:28:48 -0800 Subject: [PATCH] Fixed a bug that results in a false negative when a class is used in a type annotation for a method parameter and both the class and the method are generic and use PEP 695 syntax. This addresses #9382. --- packages/pyright-internal/src/analyzer/parseTreeUtils.ts | 7 +++++-- packages/pyright-internal/src/analyzer/typeEvaluator.ts | 5 ++--- packages/pyright-internal/src/tests/samples/typeParams3.py | 6 ++++++ packages/pyright-internal/src/tests/typeEvaluator5.test.ts | 2 +- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/pyright-internal/src/analyzer/parseTreeUtils.ts b/packages/pyright-internal/src/analyzer/parseTreeUtils.ts index 2c99118b1f69..333f3f72fd2f 100644 --- a/packages/pyright-internal/src/analyzer/parseTreeUtils.ts +++ b/packages/pyright-internal/src/analyzer/parseTreeUtils.ts @@ -1092,9 +1092,12 @@ export function getExecutionScopeNode(node: ParseNode): ExecutionScopeNode { let evaluationScope = getEvaluationScopeNode(node).node; // Classes are not considered execution scope because they are executed - // within the context of their containing module or function. Likewise, list - // comprehensions are executed within their container. + // within the context of their containing module or function. Likewise, + // list comprehensions are executed within their container. Type parameter + // scopes are special because they act as proxies for their containing + // function or class scope. while ( + evaluationScope.nodeType === ParseNodeType.TypeParameterList || evaluationScope.nodeType === ParseNodeType.Class || evaluationScope.nodeType === ParseNodeType.Comprehension ) { diff --git a/packages/pyright-internal/src/analyzer/typeEvaluator.ts b/packages/pyright-internal/src/analyzer/typeEvaluator.ts index da270a9f4e4b..e9251a070c49 100644 --- a/packages/pyright-internal/src/analyzer/typeEvaluator.ts +++ b/packages/pyright-internal/src/analyzer/typeEvaluator.ts @@ -21321,9 +21321,8 @@ export function createTypeEvaluator( // within the local scope. let scopeTypeHonorsCodeFlow = scopeType !== ScopeType.Function && scopeType !== ScopeType.Comprehension; - // Type parameter scopes don't honor code flow, but if the symbol is resolved - // using the proxy scope for the type parameter scope, we should use code flow. - if (scopeType === ScopeType.TypeParameter && symbolWithScope && symbolWithScope.scope === scope) { + // Type parameter scopes don't honor code flow. + if (symbolWithScope?.scope.type === ScopeType.TypeParameter) { scopeTypeHonorsCodeFlow = false; } diff --git a/packages/pyright-internal/src/tests/samples/typeParams3.py b/packages/pyright-internal/src/tests/samples/typeParams3.py index 172ebbb091e4..7ccad6019e61 100644 --- a/packages/pyright-internal/src/tests/samples/typeParams3.py +++ b/packages/pyright-internal/src/tests/samples/typeParams3.py @@ -101,3 +101,9 @@ def outer_method(self): def inner_func(): reveal_type(T, expected_text="complex") + + +class Outer3[T]: + # This should generate an error because Outer3 is + # not bound at this point. + def inner_func1[S](self: Outer3[S]): ... diff --git a/packages/pyright-internal/src/tests/typeEvaluator5.test.ts b/packages/pyright-internal/src/tests/typeEvaluator5.test.ts index 738911234f1e..246a9e75fa36 100644 --- a/packages/pyright-internal/src/tests/typeEvaluator5.test.ts +++ b/packages/pyright-internal/src/tests/typeEvaluator5.test.ts @@ -38,7 +38,7 @@ test('TypeParams3', () => { configOptions.defaultPythonVersion = pythonVersion3_12; const analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeParams3.py'], configOptions); - TestUtils.validateResults(analysisResults, 7); + TestUtils.validateResults(analysisResults, 8); }); test('TypeParams4', () => {