From 57ae6c6050a4a2c6a0bddd692608270fbfac7dfa Mon Sep 17 00:00:00 2001 From: A5rocks Date: Sat, 17 Jan 2026 15:26:24 +0900 Subject: [PATCH 1/5] Mark more things as literals --- mypy/checker.py | 4 ---- mypy/literals.py | 23 ++++++++++++++++++++--- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 6d70dcb90e94..39b7711eea73 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -6543,10 +6543,6 @@ def comparison_type_narrowing_helper(self, node: ComparisonExpr) -> tuple[TypeMa if ( literal(expr) == LITERAL_TYPE - and not is_literal_none(expr) - and not is_literal_not_implemented(expr) - and not is_false_literal(expr) - and not is_true_literal(expr) and not self.is_literal_enum(expr) and not ( isinstance(p_expr := get_proper_type(expr_type), CallableType) diff --git a/mypy/literals.py b/mypy/literals.py index 50720da35548..d96baa470d26 100644 --- a/mypy/literals.py +++ b/mypy/literals.py @@ -35,6 +35,7 @@ OpExpr, ParamSpecExpr, PromoteExpr, + RefExpr, RevealExpr, SetComprehension, SetExpr, @@ -44,6 +45,7 @@ SuperExpr, TempNode, TupleExpr, + TypeAlias, TypeAliasExpr, TypeApplication, TypedDictExpr, @@ -55,6 +57,7 @@ YieldExpr, YieldFromExpr, ) +from mypy.types import is_named_instance from mypy.visitor import ExpressionVisitor # [Note Literals and literal_hash] @@ -135,10 +138,24 @@ def literal(e: Expression) -> int: else: return LITERAL_NO - elif isinstance(e, NameExpr): - if isinstance(e.node, Var) and e.node.is_final and e.node.final_value is not None: + elif isinstance(e, RefExpr): + if ( + isinstance(e, NameExpr) + and isinstance(e.node, Var) + and e.node.is_final + and e.node.final_value is not None + ): + return LITERAL_YES + + NAMES = ("builtins.True", "builtins.False", "builtins.None", "builtins.NotImplemented") + if e.fullname in NAMES: return LITERAL_YES - return LITERAL_TYPE + elif isinstance(e.node, TypeAlias) and not e.node.python_3_12_type_alias: + if is_named_instance(e.node.target, NAMES): + return LITERAL_YES + + if isinstance(e, NameExpr): + return LITERAL_TYPE if isinstance(e, (IntExpr, FloatExpr, ComplexExpr, StrExpr, BytesExpr)): return LITERAL_YES From 2a869b0fbf3e2791780d4f047039d5763d052680 Mon Sep 17 00:00:00 2001 From: A5rocks Date: Fri, 23 Jan 2026 01:02:09 -0500 Subject: [PATCH 2/5] Move `NAMES` to a constant --- mypy/literals.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mypy/literals.py b/mypy/literals.py index d96baa470d26..cb6afea66005 100644 --- a/mypy/literals.py +++ b/mypy/literals.py @@ -100,6 +100,7 @@ Key: _TypeAlias = tuple[Any, ...] +LITERAL_NAMES = frozenset(("builtins.True", "builtins.False", "builtins.None", "builtins.NotImplemented")) def literal_hash(e: Expression) -> Key | None: @@ -147,11 +148,10 @@ def literal(e: Expression) -> int: ): return LITERAL_YES - NAMES = ("builtins.True", "builtins.False", "builtins.None", "builtins.NotImplemented") - if e.fullname in NAMES: + if e.fullname in LITERAL_NAMES: return LITERAL_YES elif isinstance(e.node, TypeAlias) and not e.node.python_3_12_type_alias: - if is_named_instance(e.node.target, NAMES): + if is_named_instance(e.node.target, LITERAL_NAMES): return LITERAL_YES if isinstance(e, NameExpr): From b98c82ae667cf92ed209fb7d77638ef99e9c0024 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 06:05:09 +0000 Subject: [PATCH 3/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/literals.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mypy/literals.py b/mypy/literals.py index cb6afea66005..0d86b2c238ba 100644 --- a/mypy/literals.py +++ b/mypy/literals.py @@ -100,7 +100,9 @@ Key: _TypeAlias = tuple[Any, ...] -LITERAL_NAMES = frozenset(("builtins.True", "builtins.False", "builtins.None", "builtins.NotImplemented")) +LITERAL_NAMES = frozenset( + ("builtins.True", "builtins.False", "builtins.None", "builtins.NotImplemented") +) def literal_hash(e: Expression) -> Key | None: From f3b23b187ea4c2fa9b9a5710195abc3dc3d57eef Mon Sep 17 00:00:00 2001 From: A5rocks Date: Fri, 23 Jan 2026 01:52:48 -0500 Subject: [PATCH 4/5] Expand `is_named_instance` --- mypy/types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/types.py b/mypy/types.py index 025812e25f0a..d1825da01be4 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -4040,8 +4040,8 @@ def visit_type_alias_type(self, t: TypeAliasType, /) -> list[mypy.nodes.TypeAlia return res -def is_named_instance(t: Type, fullnames: str | tuple[str, ...]) -> TypeGuard[Instance]: - if not isinstance(fullnames, tuple): +def is_named_instance(t: Type, fullnames: str | Sequence[str, ...]) -> TypeGuard[Instance]: + if isinstance(fullnames, str): fullnames = (fullnames,) t = get_proper_type(t) From d836ead299162cb31cee888409d385d5f6100805 Mon Sep 17 00:00:00 2001 From: A5rocks Date: Fri, 23 Jan 2026 02:02:34 -0500 Subject: [PATCH 5/5] ... --- mypy/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/types.py b/mypy/types.py index d1825da01be4..f4ecf0003dce 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -4040,7 +4040,7 @@ def visit_type_alias_type(self, t: TypeAliasType, /) -> list[mypy.nodes.TypeAlia return res -def is_named_instance(t: Type, fullnames: str | Sequence[str, ...]) -> TypeGuard[Instance]: +def is_named_instance(t: Type, fullnames: str | Iterable[str]) -> TypeGuard[Instance]: if isinstance(fullnames, str): fullnames = (fullnames,)