From 166c1cd5487cfd34195fcddf51754166eed66a03 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Sat, 19 Oct 2024 15:33:55 -0700 Subject: [PATCH] Fixed a recent regression that results in false positive errors under certain circumstances that involve assignability checks for two callables that involve a `*args: *tuple[]` parameter. This addresses #9238. --- .../src/analyzer/typeEvaluator.ts | 11 +++++-- .../src/languageService/completionProvider.ts | 6 +++- .../src/tests/samples/tupleUnpack2.py | 3 -- .../src/tests/samples/tupleUnpack5.py | 29 +++++++++++++++++++ .../src/tests/typeEvaluator8.test.ts | 5 ++++ 5 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 packages/pyright-internal/src/tests/samples/tupleUnpack5.py diff --git a/packages/pyright-internal/src/analyzer/typeEvaluator.ts b/packages/pyright-internal/src/analyzer/typeEvaluator.ts index 9082563ec173..f8a595070f00 100644 --- a/packages/pyright-internal/src/analyzer/typeEvaluator.ts +++ b/packages/pyright-internal/src/analyzer/typeEvaluator.ts @@ -25711,6 +25711,7 @@ export function createTypeEvaluator( } } else if ( destParam.kind !== ParamKind.Positional && + destParam.kind !== ParamKind.ExpandedArgs && srcParam.kind === ParamKind.Positional && srcParamDetails.kwargsIndex === undefined && !srcParamDetails.params.some( @@ -25853,8 +25854,10 @@ export function createTypeEvaluator( canAssign = false; } + const destParamKind = destParamDetails.params[paramIndex].kind; if ( - destParamDetails.params[paramIndex].kind !== ParamKind.Positional && + destParamKind !== ParamKind.Positional && + destParamKind !== ParamKind.ExpandedArgs && srcParamDetails.kwargsIndex === undefined ) { diag?.addMessage( @@ -25966,7 +25969,8 @@ export function createTypeEvaluator( if ( param.param.name && param.param.category === ParamCategory.Simple && - param.kind !== ParamKind.Positional + param.kind !== ParamKind.Positional && + param.kind !== ParamKind.ExpandedArgs ) { destParamMap.set(param.param.name, param); } @@ -26854,7 +26858,8 @@ export function createTypeEvaluator( ) { if ( !FunctionParam.isNameSynthesized(baseParam) && - baseParamDetails.params[i].kind !== ParamKind.Positional + baseParamDetails.params[i].kind !== ParamKind.Positional && + baseParamDetails.params[i].kind !== ParamKind.ExpandedArgs ) { diag?.addMessage( LocAddendum.overrideParamNamePositionOnly().format({ diff --git a/packages/pyright-internal/src/languageService/completionProvider.ts b/packages/pyright-internal/src/languageService/completionProvider.ts index a0bf94bda0d4..a63d9434d605 100644 --- a/packages/pyright-internal/src/languageService/completionProvider.ts +++ b/packages/pyright-internal/src/languageService/completionProvider.ts @@ -2888,7 +2888,11 @@ export class CompletionProvider { const paramDetails = getParamListDetails(type); paramDetails.params.forEach((paramInfo) => { - if (paramInfo.param.name && paramInfo.kind !== ParamKind.Positional) { + if ( + paramInfo.param.name && + paramInfo.kind !== ParamKind.Positional && + paramInfo.kind !== ParamKind.ExpandedArgs + ) { if (!SymbolNameUtils.isPrivateOrProtectedName(paramInfo.param.name)) { names.add(paramInfo.param.name); } diff --git a/packages/pyright-internal/src/tests/samples/tupleUnpack2.py b/packages/pyright-internal/src/tests/samples/tupleUnpack2.py index c88b2e82fdf6..69e4e0d62242 100644 --- a/packages/pyright-internal/src/tests/samples/tupleUnpack2.py +++ b/packages/pyright-internal/src/tests/samples/tupleUnpack2.py @@ -3,9 +3,6 @@ # it uses the * syntax instead of the backward compatibility # "Unpack" form. -from typing import Union - - def func1(v1: tuple[int, *tuple[bool, bool], str]): reveal_type(v1, expected_text="tuple[int, bool, bool, str]") diff --git a/packages/pyright-internal/src/tests/samples/tupleUnpack5.py b/packages/pyright-internal/src/tests/samples/tupleUnpack5.py new file mode 100644 index 000000000000..84d2a47eacda --- /dev/null +++ b/packages/pyright-internal/src/tests/samples/tupleUnpack5.py @@ -0,0 +1,29 @@ +# This sample tests cases where an unpacked tuple is used in +# an overload. + + +from typing import Callable, Concatenate, overload + + +@overload +def func1[**P, R](func: Callable[P, R], /, *args: *tuple[()]) -> Callable[P, R]: ... +@overload +def func1[**P, R]( + func: Callable[Concatenate[int, P], R], /, *args: *tuple[int] +) -> Callable[P, R]: ... +@overload +def func1[**P, R]( + func: Callable[Concatenate[int, int, P], R], /, *args: *tuple[int, int] +) -> Callable[P, R]: ... + + +def func1[**P, R](func: Callable[..., R], /, *args: object) -> Callable[..., R]: ... + + +@overload +def func2(*args: *tuple[int]) -> int: ... +@overload +def func2(*args: *tuple[int, int, int]) -> int: ... + + +def func2(*args: *tuple[int, *tuple[int, ...]]) -> int: ... diff --git a/packages/pyright-internal/src/tests/typeEvaluator8.test.ts b/packages/pyright-internal/src/tests/typeEvaluator8.test.ts index 9198c978c2f6..c5a5885465c6 100644 --- a/packages/pyright-internal/src/tests/typeEvaluator8.test.ts +++ b/packages/pyright-internal/src/tests/typeEvaluator8.test.ts @@ -792,6 +792,11 @@ test('TupleUnpack4', () => { TestUtils.validateResults(analysisResults1, 2); }); +test('TupleUnpack5', () => { + const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['tupleUnpack5.py']); + TestUtils.validateResults(analysisResults1, 0); +}); + test('PseudoGeneric1', () => { const analysisResults = TestUtils.typeAnalyzeSampleFiles(['pseudoGeneric1.py']);