Skip to content

Commit

Permalink
Fixed a bug that results in incorrect inferred parameter types for an…
Browse files Browse the repository at this point in the history
… unannotated method in a subclass when the parent class method contains function-scoped TypeVars. This addresses #7606.
  • Loading branch information
erictraut committed Apr 3, 2024
1 parent 4106a09 commit b6c8b7f
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 7 deletions.
9 changes: 8 additions & 1 deletion packages/pyright-internal/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17978,7 +17978,14 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
const typeVarContext = buildTypeVarContextFromSpecializedClass(
baseClassMemberInfo.classType
);
inferredParamType = applySolvedTypeVars(inferredParamType, typeVarContext);

// Add the scope of the method to handle any function-scoped TypeVars.
typeVarContext.addSolveForScope(ParseTreeUtils.getScopeIdForNode(baseClassMethodNode));

// Replace any unsolved TypeVars with Unknown (including all function-scoped TypeVars).
inferredParamType = applySolvedTypeVars(inferredParamType, typeVarContext, {
unknownIfNotFound: true,
});
}

const fileInfo = AnalyzerNodeInfo.getFileInfo(functionNode);
Expand Down
27 changes: 21 additions & 6 deletions packages/pyright-internal/src/tests/samples/paramInference2.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,34 @@

# pyright: reportIncompatibleMethodOverride=false

from typing import Generic, TypeVar
from typing import Callable, Generic, ParamSpec, TypeVar

T = TypeVar("T")
P = ParamSpec("P")
R = TypeVar("R")


class Parent(Generic[T]):
def method1(self, a: T, b: list[T]) -> None:
...
class Parent1(Generic[T]):
def method1(self, a: T, b: list[T]) -> None: ...


class Child(Parent[float]):
class Child1(Parent1[float]):
def method1(self, a, b):
reveal_type(self, expected_text="Self@Child")
reveal_type(self, expected_text="Self@Child1")
reveal_type(a, expected_text="float")
reveal_type(b, expected_text="list[float]")
return a


class Parent2:
def method1(self, fn: Callable[P, R], *args: P.args, **kwargs: P.kwargs) -> R:
return fn(*args, **kwargs)


class Child2(Parent2):
def method1(self, fn, *args, **kwargs):
reveal_type(self, expected_text="Self@Child2")
reveal_type(fn, expected_text="(...) -> Unknown")
reveal_type(args, expected_text="tuple[Unknown, ...]")
reveal_type(kwargs, expected_text="dict[str, Unknown]")
return super().method1(fn, *args, **kwargs)

0 comments on commit b6c8b7f

Please sign in to comment.