From f8e444bc499db8a948c9b18c9a65341d3d6d01e8 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Mon, 2 Dec 2024 10:50:31 -0800 Subject: [PATCH] Added support for evaluating the `slice` class type arguments for slice expressions. This addresses #9533. --- .../src/analyzer/typeEvaluator.ts | 40 ++++++++++++++----- .../src/tests/samples/slice1.py | 10 +++++ .../src/tests/typeEvaluator4.test.ts | 5 +++ 3 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 packages/pyright-internal/src/tests/samples/slice1.py diff --git a/packages/pyright-internal/src/analyzer/typeEvaluator.ts b/packages/pyright-internal/src/analyzer/typeEvaluator.ts index 9f84a158a69c..6638872b3674 100644 --- a/packages/pyright-internal/src/analyzer/typeEvaluator.ts +++ b/packages/pyright-internal/src/analyzer/typeEvaluator.ts @@ -15015,23 +15015,45 @@ export function createTypeEvaluator( } function getTypeOfSlice(node: SliceNode): TypeResult { + const noneType = getNoneType(); + let startType = noneType; + let endType = noneType; + let stepType = noneType; + let isIncomplete = false; + // Evaluate the expressions to report errors and record symbol - // references. We can skip this if we're executing speculatively. - if (!isSpeculativeModeInUse(node)) { - if (node.d.startValue) { - getTypeOfExpression(node.d.startValue); + // references. + if (node.d.startValue) { + const startTypeResult = getTypeOfExpression(node.d.startValue); + startType = startTypeResult.type; + if (startTypeResult.isIncomplete) { + isIncomplete = true; } + } - if (node.d.endValue) { - getTypeOfExpression(node.d.endValue); + if (node.d.endValue) { + const endTypeResult = getTypeOfExpression(node.d.endValue); + endType = endTypeResult.type; + if (endTypeResult.isIncomplete) { + isIncomplete = true; } + } - if (node.d.stepValue) { - getTypeOfExpression(node.d.stepValue); + if (node.d.stepValue) { + const stepTypeResult = getTypeOfExpression(node.d.stepValue); + stepType = stepTypeResult.type; + if (stepTypeResult.isIncomplete) { + isIncomplete = true; } } - return { type: getBuiltInObject(node, 'slice') }; + const sliceType = getBuiltInObject(node, 'slice'); + + if (!isClassInstance(sliceType)) { + return { type: sliceType }; + } + + return { type: ClassType.specialize(sliceType, [startType, endType, stepType]), isIncomplete }; } // Verifies that a type argument's type is not disallowed. diff --git a/packages/pyright-internal/src/tests/samples/slice1.py b/packages/pyright-internal/src/tests/samples/slice1.py new file mode 100644 index 000000000000..426fbb0f4644 --- /dev/null +++ b/packages/pyright-internal/src/tests/samples/slice1.py @@ -0,0 +1,10 @@ +# This sample tests the evaluation of slice types. + +class ClassA: + def __getitem__[T](self, item: T) -> T: + return item +a1 = ClassA() + +reveal_type(a1[::], expected_text="slice[None, None, None]") +reveal_type(a1[1:'a':False], expected_text="slice[Literal[1], Literal['a'], Literal[False]]") +reveal_type(a1[:3:5.0], expected_text="slice[None, Literal[3], float]") diff --git a/packages/pyright-internal/src/tests/typeEvaluator4.test.ts b/packages/pyright-internal/src/tests/typeEvaluator4.test.ts index 238266b9f4ef..ba5b4e5bb0a6 100644 --- a/packages/pyright-internal/src/tests/typeEvaluator4.test.ts +++ b/packages/pyright-internal/src/tests/typeEvaluator4.test.ts @@ -851,3 +851,8 @@ test('ParamSpec53', () => { const results = TestUtils.typeAnalyzeSampleFiles(['paramSpec53.py']); TestUtils.validateResults(results, 0); }); + +test('Slice1', () => { + const results = TestUtils.typeAnalyzeSampleFiles(['slice1.py']); + TestUtils.validateResults(results, 0); +});