Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix
  • Loading branch information
hauntsaninja committed Dec 30, 2025
commit 586d2a1b01051bafbccfd6ef5b00a99191806ac5
34 changes: 20 additions & 14 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -6549,6 +6549,7 @@ def comparison_type_narrowing_helper(self, node: ComparisonExpr) -> tuple[TypeMa
and not is_false_literal(expr)
and not is_true_literal(expr)
Comment on lines 6547 to +6550
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't these be LITERAL_YES? I guess maybe they aren't now, but that seems like the right distinction.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, good point. Good thing to explore in a follow up PR

and not self.is_literal_enum(expr)
and not (isinstance(expr_type, CallableType) and expr_type.is_type_obj())
):
h = literal_hash(expr)
if h is not None:
Expand Down Expand Up @@ -6658,11 +6659,7 @@ def equality_type_narrowing_helper(
# If we haven't been able to narrow types yet, we might be dealing with a
# explicit type(x) == some_type check
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this comment in the wrong place/wrong now?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess so. One of the commits I have on top of this PR gets rid of this function entirely, so won't touch for now

if_map, else_map = self.narrow_type_by_equality(
operator,
operands,
operand_types,
expr_indices,
narrowable_indices=narrowable_indices,
operator, operands, operand_types, expr_indices, narrowable_indices=narrowable_indices
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any deeper reason behind why specifically this arg and none of the others is consistently passed via kwarg?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No particularly deep reason

narrowable_operand_index_to_hash as a dict is much more complicated than narrowable_indices as a set, so I wanted to only have narrowable_operand_index_to_hash in the places you actually need a dict, so I did a little bit of renaming

)
if node is not None:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, it is more common to have things in if_map and else_map now (see testNarrowingUsingTypeVar) so we call this always

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node doesn't have an optional type, so is the condition always true?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup! I have this refactored in a later commit in my stack :-)

type_if_map, type_else_map = self.find_type_equals_check(node, expr_indices)
Expand Down Expand Up @@ -6962,12 +6959,12 @@ def refine_identity_comparison_expression(
else:
type_targets.append((i, TypeRange(expr_type, is_upper_bound=False)))

# print = lambda *a: None
# print()
# print("operands", operands)
# print("operand_types", operand_types)
# print("operator_specific_targets", operator_specific_targets)
# print("type_targets", type_targets)
if False:
print()
print("operands", operands)
print("operand_types", operand_types)
print("operator_specific_targets", operator_specific_targets)
print("type_targets", type_targets)

partial_type_maps = []

Expand Down Expand Up @@ -7000,9 +6997,18 @@ def refine_identity_comparison_expression(
else_map = {}
partial_type_maps.append((if_map, else_map))

final_if_map, final_else_map = reduce_conditional_maps(partial_type_maps, use_meet=len(operands) > 2)
# print("final_if_map", {str(k): str(v) for k, v in final_if_map.items()})
# print("final_else_map", {str(k): str(v) for k, v in final_else_map.items()})
final_if_map, final_else_map = reduce_conditional_maps(
partial_type_maps, use_meet=len(operands) > 2
)
if False:
print(
"final_if_map",
{str(k): str(v) for k, v in final_if_map.items()} if final_if_map else None,
)
print(
"final_else_map",
{str(k): str(v) for k, v in final_else_map.items()} if final_else_map else None,
)
return final_if_map, final_else_map

def refine_away_none_in_comparison(
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-narrowing.test
Original file line number Diff line number Diff line change
Expand Up @@ -2767,6 +2767,6 @@ def get_type(setting_name: str) -> Callable[[Box1], Any] | type[Any]:
def main(key: str):
existing_value_type = get_type(key)
if existing_value_type is TupleLike:
reveal_type(TupleLike) # N: Revealed type is "(def (__main__.Box1[Any]) -> Any) | type[Any]"
TupleLike(Box2("str")) # E: Argument 1 has incompatible type "Box2[str]"; expected "Box1[Any]"
reveal_type(TupleLike) # N: Revealed type is "def [_T_co] (__main__.Boxxy[_T_co`1]) -> __main__.TupleLike[_T_co`1]"
TupleLike(Box2("str"))
[builtins fixtures/tuple.pyi]
Loading