Skip to content

Regression: false positive avoid redundant cast with literal union #19055

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
JukkaL opened this issue May 8, 2025 · 4 comments
Open

Regression: false positive avoid redundant cast with literal union #19055

JukkaL opened this issue May 8, 2025 · 4 comments
Labels
bug mypy got something wrong priority-0-high

Comments

@JukkaL
Copy link
Collaborator

JukkaL commented May 8, 2025

This example started generating an error after #18588, which looks like a regression:

# mypy: warn-redundant-casts

from typing import Literal, cast

Foo = Literal["a", "b"]

class C:
    def __init__(self) -> None:
        self.x = cast(Foo, "a")  # error: Redundant cast to "Literal['a', 'b']"

I haven't looked into this in detail, but it seems possible that we infer a union type for "a" in a literal union context, which doesn't look right. If this is a correct hypothesis, #18588 wouldn't actually be the root cause, and it would just be exposing a pre-existing issue.

cc @asottile as the author of the PR

@JukkaL JukkaL added bug mypy got something wrong priority-0-high labels May 8, 2025
@sterliakov
Copy link
Collaborator

I'd say this is indeed a redundant cast as self.x doesn't have a type prior to this assignment. If you have x: SomethingIncompatible annotation, cast won't be marked redundant. How problematic is this use case for you, especially given that the fix is as trivial as self.x: Foo = 'a'?

I suspect the logic goes as follows: during the first pass we have an unknown type and infer x: Foo, then during the second pass the assignment looks like self.x: Foo = cast(Foo, "a") which is clearly a redundant cast.

@JukkaL
Copy link
Collaborator Author

JukkaL commented May 8, 2025

The reason this looked like a regression is that I've only seen this happen with literal types. This doesn't generate an error:

# mypy: warn-redundant-casts

from typing import cast

Foo = int | str

class C:
    def __init__(self) -> None:
        self.x = cast(Foo, "a")  # No error

Is there an inconsistency?

@sterliakov
Copy link
Collaborator

Indeed, that's a bit inconsistent: we only detect unnecessary casting to Literal types in this scenario (same if you annotate x: Foo directly in the body of C). That happens because we do infer literal types when in literal context, but never infer other union types when one of the union members suffices. This discrepancy isn't a new regression, it just happens to surface with --warn-redundant-casts is enabled.

I'd argue that this aspect of new mypy behavior is correct: the cast is redundant. So the actual bug is that we fail to report cast(int | str, 'a')? Given that --warn-redundant-casts is more of cosmetic lint (violation mean that the user might have missed something, not that a program is ill-formed from typing POV), I think detecting some new cases is better than not detecting, even if somewhat inconsistently.

@sterliakov
Copy link
Collaborator

Hm, no, my bad, this still looks weird. It's a positive change, but I don't understand why it actually happens.

Why do we infer a union of literals somewhere when its subset would suffice?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong priority-0-high
Projects
None yet
Development

No branches or pull requests

2 participants