8000 Fixed a bug that led to a false positive error when evaluating a call… · sourcegraph/scip-python@a37e259 · GitHub
[go: up one dir, main page]

Skip to content

Commit a37e259

Browse files
committed
Fixed a bug that led to a false positive error when evaluating a call expression that accepts multiple generic callables, at least one of which is parameterized by a ParamSpec, where the generic types of the callables interact when being solved. This addresses microsoft/pylance-release#4182.
1 parent cb86678 commit a37e259

File tree

5 files changed

+100
-155
lines changed

5 files changed

+100
-155
lines changed

packages/pyright-internal/src/analyzer/constraintSolver.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,9 +609,13 @@ export function assignTypeToTypeVar(
609609
}
610610
}
611611

612+
const adjWideTypeBound = makeConcrete
613+
? evaluator.makeTopLevelTypeVarsConcrete(curWideTypeBound, /* makeParamSpecsConcrete */ true)
614+
: curWideTypeBound;
615+
612616
if (
613617
!evaluator.assignType(
614-
makeConcrete ? evaluator.makeTopLevelTypeVarsConcrete(curWideTypeBound) : curWideTypeBound,
618+
adjWideTypeBound,
615619
newNarrowTypeBound,
616620
diag?.createAddendum(),
617621
/* destTypeVarContext */ undefined,

packages/pyright-internal/src/analyzer/typeEvaluator.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3544,7 +3544,11 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
35443544
// used as type arguments in other types) with their concrete form.
35453545
// If conditionFilter is specified and the TypeVar is a constrained
35463546
// TypeVar, only the conditions that match the filter will be included.
3547-
function makeTopLevelTypeVarsConcrete(type: Type, conditionFilter?: TypeCondition[]): Type {
3547+
function makeTopLevelTypeVarsConcrete(
3548+
type: Type,
3549+
makeParamSpecsConcrete = false,
3550+
conditionFilter?: TypeCondition[]
3551+
): Type {
35483552
return mapSubtypes(type, (subtype) => {
35493553
if (isParamSpec(subtype)) {
35503554
if (subtype.paramSpecAccess === 'args') {
@@ -3582,6 +3586,25 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
35823586
}
35833587
}
35843588

3589+
// If this is a function that contains only a ParamSpec (no additional
3590+
// parameters), convert it to a concrete type of (*args: Any, **kwargs: Any).
3591+
if (
3592+
makeParamSpecsConcrete &&
3593+
isFunction(subtype) &&
3594+
subtype.details.parameters.length === 0 &&
3595+
subtype.details.paramSpec
3596+
) {
3597+
const concreteFunction = FunctionType.createInstance(
3598+
'',
3599+
'',
3600+
'',
3601+
FunctionTypeFlags.SynthesizedMethod | FunctionTypeFlags.SkipArgsKwargsCompatibilityCheck
3602+
);
3603+
FunctionType.addDefaultParameters(concreteFunction);
3604+
3605+
return FunctionType.cloneForParamSpec(subtype, concreteFunction);
3606+
}
3607+
35853608
// If this is a TypeVarTuple *Ts, convert it to an unpacked tuple
35863609
// *tuple[*Ts].
35873610
if (isVariadicTypeVar(subtype)) {
@@ -7152,7 +7175,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
71527175
// Expand constrained type variables.
71537176
if (isTypeVar(setType) && setType.details.constraints.length > 0) {
71547177
const conditionFilter = isClassInstance(baseType) ? baseType.condition : undefined;
7155-
setType = makeTopLevelTypeVarsConcrete(setType, conditionFilter);
7178+
setType = makeTopLevelTypeVarsConcrete(setType, /* makeParamSpecsConcrete */ undefined, conditionFilter);
71567179
}
71577180

71587181
argList.push({

packages/pyright-internal/src/analyzer/typeEvaluatorTypes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ export interface TypeEvaluator {
417417
getTypeOfArgument: (arg: FunctionArgument) => TypeResult;
418418
markNamesAccessed: (node: ParseNode, names: string[]) => void;
419419
getScopeIdForNode: (node: ParseNode) => string;
420-
makeTopLevelTypeVarsConcrete: (type: Type) => Type;
420+
makeTopLevelTypeVarsConcrete: (type: Type, makeParamSpecsConcrete?: boolean) => Type;
421421
mapSubtypesExpandTypeVars: (
422422
type: Type,
423423
conditionFilter: TypeCondition[] | undefined,
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# This sample tests the interaction between a generic callable parameterized
2+
# with a ParamSpec and another generic callable that is parameterized
3+
# with a TypeVar.
4+
5+
from typing import Callable, ParamSpec, TypeVar
6+
7+
_P = ParamSpec("_P")
8+
_R = TypeVar("_R")
9+
10+
11+
def call(obj: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> _R:
12+
return obj(*args, **kwargs)
13+
14+
15+
def func1():
16+
return 0
17+
18+
19+
def func2():
20+
return 0.0
21+
22+
23+
result1 = map(call, [func1])
24+
reveal_type(result1, expected_text="map[int]")
25+
26+
result2 = map(call, [func1, func2])
27+
reveal_type(result2, expected_text="map[float]")

0 commit comments

Comments
 (0)
0