From 8ce23eb615ace823cd45d9c4934c316c58cfe0e3 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Sun, 18 Jun 2023 12:55:22 -0700 Subject: [PATCH] =?UTF-8?q?Improved=20`reportUnnecessaryCast`=20so=20it=20?= =?UTF-8?q?works=20with=20types=20other=20than=20cl=E2=80=A6=20(#5336)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improved `reportUnnecessaryCast` so it works with types other than class instances. This addresses https://github.com/microsoft/pyright/issues/5333. --- .../src/analyzer/typeEvaluator.ts | 4 +- .../src/tests/checker.test.ts | 2 +- .../src/tests/samples/unnecessaryCast1.py | 51 +++++++++++++++++-- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/packages/pyright-internal/src/analyzer/typeEvaluator.ts b/packages/pyright-internal/src/analyzer/typeEvaluator.ts index e86c7a37b864..594d1966ab57 100644 --- a/packages/pyright-internal/src/analyzer/typeEvaluator.ts +++ b/packages/pyright-internal/src/analyzer/typeEvaluator.ts @@ -8913,9 +8913,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions // Verify that the cast is necessary. const castToType = getTypeOfArgumentExpectingType(argList[0]).type; const castFromType = getTypeOfArgument(argList[1]).type; - if (isInstantiableClass(castToType) && isClassInstance(castFromType)) { + if (TypeBase.isInstantiable(castToType) && !isUnknown(castToType)) { if ( - isTypeSame(castToType, ClassType.cloneAsInstantiable(castFromType), { + isTypeSame(convertToInstance(castToType), castFromType, { ignorePseudoGeneric: true, }) ) { diff --git a/packages/pyright-internal/src/tests/checker.test.ts b/packages/pyright-internal/src/tests/checker.test.ts index 24764cb1c36b..f420585e991e 100644 --- a/packages/pyright-internal/src/tests/checker.test.ts +++ b/packages/pyright-internal/src/tests/checker.test.ts @@ -261,7 +261,7 @@ test('UnnecessaryCast1', () => { // Turn on errors. configOptions.diagnosticRuleSet.reportUnnecessaryCast = 'error'; analysisResults = TestUtils.typeAnalyzeSampleFiles(['unnecessaryCast1.py'], configOptions); - TestUtils.validateResults(analysisResults, 1); + TestUtils.validateResults(analysisResults, 6); }); test('UnnecessaryContains1', () => { diff --git a/packages/pyright-internal/src/tests/samples/unnecessaryCast1.py b/packages/pyright-internal/src/tests/samples/unnecessaryCast1.py index fe3d447e3c8e..f63eddabb27d 100644 --- a/packages/pyright-internal/src/tests/samples/unnecessaryCast1.py +++ b/packages/pyright-internal/src/tests/samples/unnecessaryCast1.py @@ -1,13 +1,54 @@ # This sample tests the type checker's reportUnnecessaryCast feature. -from typing import cast, Union +from typing import Never, NoReturn, TypeVar, cast -def foo(a: int): +def func1(a: int): # This should generate an error if # reportUnnecessaryCast is enabled. - b = cast(int, a) + v1 = cast(int, a) -c: Union[int, str] = "hello" -d = cast(int, c) +def func2(a: int | str): + v1 = cast(int, a) + + b: str = "hello" + v2 = cast(int, b) + + +def func3(a: int | None): + v1 = cast(int, a) + + # This should generate an error if + # reportUnnecessaryCast is enabled. + v2 = cast(int | None, a) + + +T = TypeVar("T") + + +def func4(a: list[T]) -> list[T]: + # This should generate an error if + # reportUnnecessaryCast is enabled. + v1 = cast(list[T], a) + + return a + + +def func5(a: Never): + # This should generate an error if + # reportUnnecessaryCast is enabled. + v1 = cast(NoReturn, a) + + +def func6(a: type[int], b: int): + v1 = cast(int, a) + v2 = cast(type[int], b) + + # This should generate an error if + # reportUnnecessaryCast is enabled. + v3 = cast(type[int], a) + + # This should generate an error if + # reportUnnecessaryCast is enabled. + v4 = cast(int, b)