From 7d410cfbeebbbbcbb2918017293759bf8436403b Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Sun, 7 Jul 2024 10:06:08 -0700 Subject: [PATCH] Improved handling of tuple expressions used in type expressions that involve specialization, such as `dict[()]`. (#8329) --- .../src/analyzer/typeEvaluator.ts | 26 +++++++++---------- .../src/tests/samples/typeVarDefaultClass1.py | 25 +++++++++++------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/packages/pyright-internal/src/analyzer/typeEvaluator.ts b/packages/pyright-internal/src/analyzer/typeEvaluator.ts index 4effd00907..83ed452ca5 100644 --- a/packages/pyright-internal/src/analyzer/typeEvaluator.ts +++ b/packages/pyright-internal/src/analyzer/typeEvaluator.ts @@ -47,6 +47,7 @@ import { ConstantNode, DecoratorNode, DictionaryNode, + ErrorExpressionCategory, ExceptNode, ExpressionNode, ForNode, @@ -1397,11 +1398,6 @@ export function createTypeEvaluator( return; } - // Exempt empty tuples, which can be used for specializing a TypeVarTuple. - if (isClassInstance(typeResult.type) && typeResult.type.tupleTypeArguments?.length === 0) { - return; - } - const diag = new DiagnosticAddendum(); if (isUnion(typeResult.type)) { doForEachSubtype(typeResult.type, (subtype) => { @@ -1941,11 +1937,9 @@ export function createTypeEvaluator( return false; } - // Check for Tuple[()] (an empty tuple). - if (isTupleClass(type)) { - if (type.tupleTypeArguments && type.tupleTypeArguments.length === 0) { - return false; - } + // // Check for tuple[()] (an empty tuple). + if (type.tupleTypeArguments && type.tupleTypeArguments.length === 0) { + return false; } // Check for Literal[False], Literal[0], Literal[""]. @@ -7642,13 +7636,12 @@ export function createTypeEvaluator( return typeResult; }; - // A single (non-empty) tuple is treated the same as a list of items in the index. + // A tuple is treated the same as a list of items in the index. if ( node.items.length === 1 && !node.trailingComma && !node.items[0].name && - node.items[0].valueExpression.nodeType === ParseNodeType.Tuple && - node.items[0].valueExpression.expressions.length > 0 + node.items[0].valueExpression.nodeType === ParseNodeType.Tuple ) { node.items[0].valueExpression.expressions.forEach((item, index) => { typeArgs.push(getTypeArgTypeResult(item, index)); @@ -7682,7 +7675,12 @@ export function createTypeEvaluator( addError(LocMessage.keywordArgInTypeArgument(), arg.valueExpression); } - typeArgs.push(typeResult); + if ( + arg.valueExpression.nodeType !== ParseNodeType.Error || + arg.valueExpression.category !== ErrorExpressionCategory.MissingIndexOrSlice + ) { + typeArgs.push(typeResult); + } }); } diff --git a/packages/pyright-internal/src/tests/samples/typeVarDefaultClass1.py b/packages/pyright-internal/src/tests/samples/typeVarDefaultClass1.py index 6a1170dd55..9cda7df801 100644 --- a/packages/pyright-internal/src/tests/samples/typeVarDefaultClass1.py +++ b/packages/pyright-internal/src/tests/samples/typeVarDefaultClass1.py @@ -3,14 +3,14 @@ # generic classes. from typing import Generic, Self, assert_type + from typing_extensions import ( # pyright: ignore[reportMissingModuleSource] - TypeVar, ParamSpec, + TypeVar, TypeVarTuple, Unpack, ) - T1 = TypeVar("T1") T2 = TypeVar("T2", default=int) T3 = TypeVar("T3", default=str) @@ -26,10 +26,11 @@ def method1(self) -> Self: ) -def func_a1(a: ClassA1, b: ClassA1[float], c: ClassA1[float, float]): +def func_a1(a: ClassA1, b: ClassA1[float], c: ClassA1[float, float], d: ClassA1[()]): reveal_type(a, expected_text="ClassA1[int, str]") reveal_type(b, expected_text="ClassA1[float, str]") reveal_type(c, expected_text="ClassA1[float, float]") + reveal_type(d, expected_text="ClassA1[int, str]") class ClassA2(Generic[T1, T2, T3]): @@ -60,7 +61,8 @@ def func_a2( P3 = ParamSpec("P3", default=...) -class ClassB1(Generic[P2, P3]): ... +class ClassB1(Generic[P2, P3]): + ... def func_b1(a: ClassB1, b: ClassB1[[float]], c: ClassB1[[float], [float]]): @@ -75,13 +77,16 @@ def func_b1(a: ClassB1, b: ClassB1[[float]], c: ClassB1[[float], [float]]): Ts4 = TypeVarTuple("Ts4", default=Unpack[tuple[()]]) -class ClassC1(Generic[*Ts2]): ... +class ClassC1(Generic[*Ts2]): + ... -class ClassC2(Generic[T3, *Ts3]): ... +class ClassC2(Generic[T3, *Ts3]): + ... -class ClassC3(Generic[T3, *Ts4]): ... +class ClassC3(Generic[T3, *Ts4]): + ... def func_c1(a: ClassC1, b: ClassC1[*tuple[float]]): @@ -106,7 +111,8 @@ def func_c3(a: ClassC3, b: ClassC3[int], c: ClassC3[int, *tuple[float]]): Ts5 = TypeVarTuple("Ts5") -class ClassD(Generic[*Ts5, P4, P5]): ... # OK +class ClassD(Generic[*Ts5, P4, P5]): + ... # OK reveal_type( @@ -121,7 +127,8 @@ class ClassD(Generic[*Ts5, P4, P5]): ... # OK P6 = ParamSpec("P6", default=[str, int]) -class ClassE(Generic[P6]): ... +class ClassE(Generic[P6]): + ... assert_type(ClassE, type[ClassE[str, int]])