mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 07:55:56 +03:00
Fixed a bug that results in a false negative when a generic function has a parameter with a generic type and a default argument value. This could lead to a typing hole when considering subtyping rules for callables. This addresses #7288. (#7954)
This commit is contained in:
parent
0b7860b15e
commit
dbac83d45c
@ -25073,6 +25073,28 @@ export function createTypeEvaluator(
|
||||
);
|
||||
canAssign = false;
|
||||
}
|
||||
} else {
|
||||
// Assign default arg values in case they are needed for
|
||||
// populating TypeVar constraints.
|
||||
for (let i = destParamDetails.firstPositionOrKeywordIndex; i < srcPositionalCount; i++) {
|
||||
const paramInfo = srcParamDetails.params[i];
|
||||
const defaultArgType = paramInfo.defaultArgType ?? paramInfo.param.defaultType;
|
||||
|
||||
if (
|
||||
defaultArgType &&
|
||||
!assignType(
|
||||
paramInfo.type,
|
||||
defaultArgType,
|
||||
diag?.createAddendum(),
|
||||
srcTypeVarContext,
|
||||
/* destTypeVarContext */ undefined,
|
||||
flags,
|
||||
recursionCount
|
||||
)
|
||||
) {
|
||||
canAssign = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Make sure the remaining positional arguments are of the
|
||||
@ -25276,6 +25298,26 @@ export function createTypeEvaluator(
|
||||
) {
|
||||
canAssign = false;
|
||||
}
|
||||
} else if (srcParamInfo.param.hasDefault) {
|
||||
// Assign default arg values in case they are needed for
|
||||
// populating TypeVar constraints.
|
||||
const defaultArgType =
|
||||
srcParamInfo.defaultArgType ?? srcParamInfo.param.defaultType;
|
||||
|
||||
if (
|
||||
defaultArgType &&
|
||||
!assignType(
|
||||
srcParamInfo.type,
|
||||
defaultArgType,
|
||||
diag?.createAddendum(),
|
||||
srcTypeVarContext,
|
||||
/* destTypeVarContext */ undefined,
|
||||
flags,
|
||||
recursionCount
|
||||
)
|
||||
) {
|
||||
canAssign = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const destParamType = destParamInfo.type;
|
||||
|
47
packages/pyright-internal/src/tests/samples/call15.py
Normal file
47
packages/pyright-internal/src/tests/samples/call15.py
Normal file
@ -0,0 +1,47 @@
|
||||
# This sample tests the case where a generic function has a default argument
|
||||
# value for a parameter with a generic type.
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Iterable, Mapping, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
default_value: dict[str, int] = {}
|
||||
|
||||
|
||||
def func1(x: T, y: Mapping[str, T] = default_value, /) -> T: ...
|
||||
def func2(x: T, y: Mapping[str, T] = default_value) -> T: ...
|
||||
def func3(x: T, *, y: Mapping[str, T] = default_value) -> T: ...
|
||||
|
||||
|
||||
def test1(func: Callable[[T], T], value: T) -> T:
|
||||
return func(value)
|
||||
|
||||
|
||||
# This should generate an error.
|
||||
test1(func1, "")
|
||||
|
||||
# This should generate an error.
|
||||
test1(func2, "")
|
||||
|
||||
# This should generate an error.
|
||||
test1(func3, "")
|
||||
|
||||
reveal_type(test1(func1, 1), expected_text="int")
|
||||
reveal_type(test1(func2, 1), expected_text="int")
|
||||
reveal_type(test1(func3, 1), expected_text="int")
|
||||
|
||||
|
||||
def func4(x: T, y: Iterable[T] = default_value, z: T = "", /) -> T: ...
|
||||
def func5(x: T, y: Iterable[T] = default_value, z: T = "") -> T: ...
|
||||
def func6(x: T, *, y: Iterable[T] = default_value, z: T = "") -> T: ...
|
||||
|
||||
|
||||
# This should generate an error.
|
||||
test1(func4, 1)
|
||||
|
||||
# This should generate an error.
|
||||
test1(func5, 1)
|
||||
|
||||
# This should generate an error.
|
||||
test1(func6, 1)
|
@ -209,7 +209,9 @@ _T9 = TypeVar("_T9")
|
||||
|
||||
|
||||
@overload
|
||||
def func12(func: Callable[[_T7], _T8], iterable: Iterable[_T7], /) -> Iterable[_T8]: ...
|
||||
def func12(
|
||||
func: Callable[[_T7], _T8], iterable: Iterable[_T7], default_value: None = None, /
|
||||
) -> Iterable[_T8 | None]: ...
|
||||
|
||||
|
||||
@overload
|
||||
|
@ -858,6 +858,12 @@ test('Call14', () => {
|
||||
TestUtils.validateResults(analysisResults, 5);
|
||||
});
|
||||
|
||||
test('Call15', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['call15.py']);
|
||||
|
||||
TestUtils.validateResults(analysisResults, 6);
|
||||
});
|
||||
|
||||
test('Function1', () => {
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['function1.py']);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user