Skip to content

Conversation

@visz11
Copy link
Collaborator

@visz11 visz11 commented Nov 21, 2025

CodeAnt-AI Description

Handle combined boolean checks when inferring reachability

What Changed

  • Reachability now evaluates not, and, and or expressions by checking both operands so dual platform guards and their negations yield precise reachability outcomes instead of relying on short-circuit assumptions
  • Unhandled operators fall back to unknown, preventing incorrect reachability decisions for unsupported expressions
  • Added regression tests covering binary, negated, and TYPE_CHECKING/MYPY combinations plus unsupported operators to lock in the corrected behavior

Impact

✅ Clearer unreachable-code detection
✅ More accurate missing-return reports for TYPE_CHECKING guards
✅ Better type reveals for platform-dependent conditions

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved unreachable code detection through enhanced conditional expression evaluation and logical operator handling
    • Better type narrowing for complex conditional statements and short-circuit logic
  • Tests

    • Expanded test coverage for unreachable code analysis across various conditional patterns and platform checks

✏️ Tip: You can customize this high-level summary in your review settings.

@codeant-ai
Copy link

codeant-ai bot commented Nov 21, 2025

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@gemini-code-assist
Copy link

Summary of Changes

Hello @visz11, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refines the infer_condition_value function within Mypy's reachability analysis module. The primary goal is to enhance the accuracy of truth value inference for boolean expressions, particularly those involving not operators and complex and/or combinations. This leads to more precise unreachable code detection and type checking, especially in platform-specific or type-checking-specific conditional blocks, ensuring Mypy provides more reliable feedback.

Highlights

  • Improved not operator handling: The infer_condition_value function now correctly processes not expressions by recursively evaluating the operand and then inverting its inferred truth value.
  • Enhanced and/or expression evaluation: The logic for and and or operations has been refactored to more accurately determine the combined truth value, considering all possible states (e.g., ALWAYS_TRUE, MYPY_TRUE, ALWAYS_FALSE, MYPY_FALSE) for both operands, rather than relying on simple short-circuiting logic.
  • Comprehensive test coverage: New unit tests have been added to validate the improved handling of binary operations and negation in conditional value inference, including cases involving sys.platform, TYPE_CHECKING, and MYPY constants.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Nov 21, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Refactors mypy's condition value inference logic to explicitly handle unary negation and binary boolean operators using truth-table-driven evaluation, replacing prior ad-hoc negation tracking, with comprehensive unit test additions covering conditional evaluation, short-circuit logic, and unreachable code scenarios.

Changes

Cohort / File(s) Summary
Core reachability logic
mypy/reachability.py
Rewrites infer_condition_value() to directly evaluate unary not expressions and binary and/or operators via truth-table mapping; removes negation-tracking flag logic; adds early guard for unsupported OpExpr operators.
Unreachable code test coverage
test-data/unit/check-unreachable-code.test
Adds seven new test cases for conditional evaluation with type narrowing: binary ops, negation, unsupported operators, MYPY_FALSE flags, and short-circuit and/or assignments with conditional branches.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant infer_condition_value
    participant Truth_Table
    
    Caller->>infer_condition_value: expr: Expression
    
    alt Unary Not
        infer_condition_value->>infer_condition_value: Evaluate inner expression
        infer_condition_value->>Truth_Table: inverted_truth_mapping
        Truth_Table-->>infer_condition_value: Inverted result
    else Binary And/Or
        infer_condition_value->>infer_condition_value: Evaluate left side
        infer_condition_value->>infer_condition_value: Evaluate right side
        infer_condition_value->>Truth_Table: Look up (left, right, operator)
        Truth_Table-->>infer_condition_value: Combined truth value
    else Unsupported Op
        infer_condition_value-->>Caller: TRUTH_VALUE_UNKNOWN
    else Other Expr
        infer_condition_value->>infer_condition_value: Direct evaluation
        infer_condition_value-->>Caller: Result
    end
    
    infer_condition_value-->>Caller: int (truth value)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • mypy/reachability.py: Logic-dense rewrite of boolean evaluation with truth-table mappings requires careful verification of operator combinations and edge cases
  • check-unreachable-code.test: Seven new test cases with type-reveal assertions need verification that they correctly validate the new inference behavior
  • Attention areas:
    • Truth-table logic for and/or operator combinations with all TRUTH_VALUE_* variants
    • Negation inversion consistency in the new direct evaluation path
    • Test case assertions align with expected type narrowing under different conditions

Poem

A rabbit hops through logic true,
Negations flipped and "and/or" too,
With truth tables bright and clean,
Clearer paths have never been! 🐰✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Clone bugfix/gh 18901 reachability' lacks clarity and specificity about the actual changes made to the codebase. Use a more descriptive title that explains the primary change, such as 'Refactor infer_condition_value to improve boolean operator handling' or 'Fix reachability analysis for conditional values and binary operators'.
✅ Passed checks (2 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch clone-bugfix/gh-18901-reachability

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@visz11
Copy link
Collaborator Author

visz11 commented Nov 21, 2025

@refacto-visz

@codeant-ai codeant-ai bot added the size:L This PR changes 100-499 lines, ignoring generated files label Nov 21, 2025
@refacto-visz
Copy link

refacto-visz bot commented Nov 21, 2025

fix: Improve reachability analysis for compound boolean expressions

TL;DR: This PR fixes a bug in mypy's reachability analysis by correctly evaluating compound boolean expressions involving and, or, and not in conditional statements.


Refacto PR Summary

This change overhauls the logic within mypy/reachability.py for determining the truthiness of conditional expressions. The previous implementation was flawed, as it only evaluated the left-hand side of and/or expressions in many cases, leading to incorrect reachability analysis. For example, an expression like False or True would be incorrectly evaluated as False.

The infer_condition_value function has been refactored to:

  1. Handle not expressions recursively at the beginning of the function for cleaner logic.
  2. Fully evaluate both the left and right operands of and and or expressions.
  3. Apply a comprehensive set of rules to combine the truthiness results of both operands, correctly handling combinations of compile-time constants (TYPE_CHECKING), platform/version checks, and user-defined flags.

This enhancement allows mypy to more accurately detect unreachable code, preventing false positives and correctly identifying errors in code blocks that are truly reachable.

Change Highlights

Click to expand
  • mypy/reachability.py: Completely rewrote the logic for and and or operators in infer_condition_value to evaluate both sides of the expression and correctly combine their truth values. The handling of not was also improved.
  • test-data/unit/check-unreachable-code.test: Added extensive new test suites (testConditionalValuesBinaryOps, testConditionalValuesNegation, etc.) to verify the corrected logic for various combinations of boolean operators and conditions, including interactions with TYPE_CHECKING.

Sequence Diagram

sequenceDiagram
    participant Mypy as Mypy Checker
    participant R as reachability.py
    
    Mypy->>R: infer_condition_value(expr='A and B', ...)
    R->>R: Recurse: infer_condition_value(expr='A', ...)
    R-->>R: Return truth_value_A
    R->>R: Recurse: infer_condition_value(expr='B', ...)
    R-->>R: Return truth_value_B
    R->>R: Combine truth_value_A and truth_value_B based on 'and' logic
    R-->>Mypy: Return final combined truth value
Loading

Testing Guide

Click to expand
  1. Verify and logic: Create a Python file with the following content:

    # test.py
    import sys
    
    if sys.platform == 'win32' and sys.platform == 'linux':
        x = 1
    else:
        x = 's'
    reveal_type(x)

    Run mypy --platform linux test.py. The revealed type for x should be Literal['s'], as mypy should determine the if condition is always false.

  2. Verify or logic: Create a Python file with the following content:

    # test.py
    import sys
    from typing import TYPE_CHECKING
    
    if sys.platform == 'linux' or TYPE_CHECKING:
        def my_func() -> int:
            ... # No return

    Run mypy --platform linux test.py. Mypy should report a "Missing return statement" error, as it correctly identifies the condition as always true during type checking.

  3. Verify not logic: Create a Python file with the following content:

    # test.py
    import sys
    
    if not (sys.platform == 'win32' or sys.platform == 'darwin'):
        y = 1
    else:
        y = 's'
    reveal_type(y)

    Run mypy --platform linux test.py. The revealed type for y should be Literal[1], as the condition is true.

@codeant-ai
Copy link

codeant-ai bot commented Nov 21, 2025

CodeAnt AI finished reviewing your PR.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the logic for inferring the truth value of conditional expressions in mypy/reachability.py, specifically for and, or, and not operators. The new implementation is more robust and correct, especially for binary logical operators, as it now evaluates both operands to determine the result. The handling of not expressions has also been simplified. The new test cases in test-data/unit/check-unreachable-code.test are thorough and provide good coverage for the changes. Overall, this is a solid improvement. I've left one minor suggestion to remove a redundant check.

Comment on lines +131 to +133
if expr.op not in ("or", "and"):
return TRUTH_VALUE_UNKNOWN

Choose a reason for hiding this comment

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

medium

This check is redundant because the elif condition on line 130 already ensures that expr.op is either 'and' or 'or'. You can remove these lines.

@refacto-visz
Copy link

refacto-visz bot commented Nov 21, 2025

Refacto is reviewing this PR. Please wait for the review comments to be posted.

@refacto-visz
Copy link

refacto-visz bot commented Nov 21, 2025

Code Review: Reachability Logic Enhancements

👍 Well Done
Improved Logic and Test Coverage

The refactoring of the boolean operator evaluation in infer_condition_value and the addition of comprehensive tests significantly improve the accuracy and robustness of the reachability analysis.

📁 Selected files for review (2)
  • mypy/reachability.py
  • test-data/unit/check-unreachable-code.test
🎯 Custom Instructions
✅ Applied Instructions
Organization Guidelines
  • Use feature flags for new functionality and include a clear rollback plan.
  • Very critical to consider apply pagination to queries
  • Follow the company security checklist:
    • No hard-coded secrets or credentials.
    • Validate all external inputs.
    • Use parameterized queries for DB access.

Scope: All files

📝 Additional Comments
mypy/reachability.py (1)
Redundant Conditional Check

This conditional check is redundant because the parent elif block on line 130 already guarantees that expr.op is one of 'and' or 'or'. This code is unreachable and can be safely removed to improve clarity.

Standards:

  • ISO-IEC-25010-Functional-Correctness-Appropriateness

Comment on lines +137 to +155
if expr.op == "or":
if ALWAYS_TRUE in results:
return ALWAYS_TRUE
elif MYPY_TRUE in results:
return MYPY_TRUE
elif left == right == MYPY_FALSE:
return MYPY_FALSE
elif results <= {ALWAYS_FALSE, MYPY_FALSE}:
return ALWAYS_FALSE
elif expr.op == "and":
if ALWAYS_FALSE in results:
return ALWAYS_FALSE
elif MYPY_FALSE in results:
return MYPY_FALSE
elif left == right == ALWAYS_TRUE:
return ALWAYS_TRUE
elif results <= {ALWAYS_TRUE, MYPY_TRUE}:
return MYPY_TRUE
return TRUTH_VALUE_UNKNOWN
Copy link

Choose a reason for hiding this comment

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

Incorrect Boolean Combination Logic

The logic for combining truth values for or and and operations is incorrect for several combinations. For example, MYPY_TRUE or MYPY_FALSE should evaluate to ALWAYS_TRUE, but the code returns MYPY_TRUE. Similarly, MYPY_FALSE and MYPY_TRUE should be ALWAYS_FALSE, but it returns MYPY_FALSE. This introduces functional correctness bugs into the reachability analysis.

        if expr.op == "or":
            if ALWAYS_TRUE in results:
                return ALWAYS_TRUE
            if TRUTH_VALUE_UNKNOWN in results:
                return TRUTH_VALUE_UNKNOWN
            if results == {MYPY_TRUE, MYPY_FALSE}:
                return ALWAYS_TRUE
            if MYPY_TRUE in results:
                return MYPY_TRUE
            if MYPY_FALSE in results:
                return MYPY_FALSE
            return ALWAYS_FALSE  # Both operands are ALWAYS_FALSE
        elif expr.op == "and":
            if ALWAYS_FALSE in results:
                return ALWAYS_FALSE
            if TRUTH_VALUE_UNKNOWN in results:
                return TRUTH_VALUE_UNKNOWN
            if results == {MYPY_TRUE, MYPY_FALSE}:
                return ALWAYS_FALSE
            if MYPY_FALSE in results:
                return MYPY_FALSE
            if MYPY_TRUE in results:
                return MYPY_TRUE
            return ALWAYS_TRUE  # Both operands are ALWAYS_TRUE
        return TRUTH_VALUE_UNKNOWN
Commitable Suggestion
Suggested change
if expr.op == "or":
if ALWAYS_TRUE in results:
return ALWAYS_TRUE
elif MYPY_TRUE in results:
return MYPY_TRUE
elif left == right == MYPY_FALSE:
return MYPY_FALSE
elif results <= {ALWAYS_FALSE, MYPY_FALSE}:
return ALWAYS_FALSE
elif expr.op == "and":
if ALWAYS_FALSE in results:
return ALWAYS_FALSE
elif MYPY_FALSE in results:
return MYPY_FALSE
elif left == right == ALWAYS_TRUE:
return ALWAYS_TRUE
elif results <= {ALWAYS_TRUE, MYPY_TRUE}:
return MYPY_TRUE
return TRUTH_VALUE_UNKNOWN
if expr.op == "or":
if ALWAYS_TRUE in results:
return ALWAYS_TRUE
if TRUTH_VALUE_UNKNOWN in results:
return TRUTH_VALUE_UNKNOWN
if results == {MYPY_TRUE, MYPY_FALSE}:
return ALWAYS_TRUE
if MYPY_TRUE in results:
return MYPY_TRUE
if MYPY_FALSE in results:
return MYPY_FALSE
return ALWAYS_FALSE # Both operands are ALWAYS_FALSE
elif expr.op == "and":
if ALWAYS_FALSE in results:
return ALWAYS_FALSE
if TRUTH_VALUE_UNKNOWN in results:
return TRUTH_VALUE_UNKNOWN
if results == {MYPY_TRUE, MYPY_FALSE}:
return ALWAYS_FALSE
if MYPY_FALSE in results:
return MYPY_FALSE
if MYPY_TRUE in results:
return MYPY_TRUE
return ALWAYS_TRUE # Both operands are ALWAYS_TRUE
return TRUTH_VALUE_UNKNOWN
Standards
  • ISO-IEC-25010-Functional-Correctness-Appropriateness

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
test-data/unit/check-unreachable-code.test (1)

553-577: Nice coverage of MYPY/TYPE_CHECKING in binary ops; consider adding a mixed-negation case

testMypyFalseValuesInBinaryOps_no_empty does a good job checking that:

  • TYPE_CHECKING/MYPY combined with sys.platform == 'linux' under and produce mypy-only guarded defs (foo1foo4), and
  • the same under or produce always-taken branches that must satisfy normal return checking (bar1bar4).

Given the updated truth table, an additional case like:

  • if TYPE_CHECKING or not TYPE_CHECKING: ...

(or a similar MYPY variant) would help pin down the behavior when mixing MYPY_TRUE and MYPY_FALSE in the same or expression, which is currently where the implementation has the most subtle edge cases.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 380cb8d and 648bd84.

📒 Files selected for processing (2)
  • mypy/reachability.py (1 hunks)
  • test-data/unit/check-unreachable-code.test (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
mypy/reachability.py (1)
mypy/nodes.py (17)
  • expr (2290-2296)
  • UnaryExpr (2052-2071)
  • name (234-235)
  • name (353-354)
  • name (532-533)
  • name (571-577)
  • name (809-810)
  • name (907-908)
  • name (1068-1069)
  • name (2287-2288)
  • name (2572-2573)
  • name (3205-3207)
  • name (3752-3753)
  • name (3864-3865)
  • NameExpr (1875-1895)
  • MemberExpr (1898-1914)
  • OpExpr (2090-2133)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
  • GitHub Check: Test suite with py313-ubuntu, mypyc-compiled
  • GitHub Check: Formatting and code style with Black + ruff
  • GitHub Check: mypyc runtime tests with py39-macos
  • GitHub Check: Run mypy_primer (2)
  • GitHub Check: Test suite with py312-ubuntu, mypyc-compiled
  • GitHub Check: Run mypy_primer (3)
  • GitHub Check: Type check our own code (py39-ubuntu)
  • GitHub Check: Run mypy_primer (1)
  • GitHub Check: Run mypy_primer (0)
  • GitHub Check: Type check our own code (py39-windows-64)
  • GitHub Check: Run mypy_primer (4)
  • GitHub Check: Test suite with py39-windows-64
  • GitHub Check: Test suite with py310-ubuntu
  • GitHub Check: Test suite with py311-ubuntu, mypyc-compiled
  • GitHub Check: Test suite with py39-ubuntu, mypyc-compiled
  • GitHub Check: Test mypyc suite with 32-bit Python
🔇 Additional comments (4)
mypy/reachability.py (1)

118-121: Unary not handling looks good and is simpler than the old negation tracking

Early-special-casing UnaryExpr("not", ...) and inverting via inverted_truth_mapping keeps the logic straightforward and correctly composes with the existing NameExpr / MemberExpr / ComparisonExpr paths. No issues spotted here.

test-data/unit/check-unreachable-code.test (3)

503-523: Binary-op conditional tests nicely cover all true/false combinations

testConditionalValuesBinaryOps exercises the full matrix of and/or combinations for sys.platform == 'linux' / 'windows' under a fixed --platform linux, and the expected reveal_type results line up with the new reachability logic for ALWAYS_TRUE vs ALWAYS_FALSE. Looks solid.


525-542: Negation tests give good coverage for not over simple expressions and and/or groups

testConditionalValuesNegation validates that unary not on both primitive platform comparisons and grouped and/or expressions produces the expected literal vs bool types, which should catch regressions in the new UnaryExpr("not") handling and its interaction with the boolean truth table. No issues here.


543-552: Unsupported-op tests correctly assert we don't over-special-case arbitrary operators

testConditionalValuesUnsupportedOps ensures that non-boolean operators like unary/binary - on conditions are left to normal type inference and not fed into the reachability truth table, with the expected Union[Literal[0], str] results. This is a useful guardrail against over-eager condition folding.

Comment on lines +131 to +155
if expr.op not in ("or", "and"):
return TRUTH_VALUE_UNKNOWN

left = infer_condition_value(expr.left, options)
if (left in (ALWAYS_TRUE, MYPY_TRUE) and expr.op == "and") or (
left in (ALWAYS_FALSE, MYPY_FALSE) and expr.op == "or"
):
# Either `True and <other>` or `False or <other>`: the result will
# always be the right-hand-side.
return infer_condition_value(expr.right, options)
else:
# The result will always be the left-hand-side (e.g. ALWAYS_* or
# TRUTH_VALUE_UNKNOWN).
return left
right = infer_condition_value(expr.right, options)
results = {left, right}
if expr.op == "or":
if ALWAYS_TRUE in results:
return ALWAYS_TRUE
elif MYPY_TRUE in results:
return MYPY_TRUE
elif left == right == MYPY_FALSE:
return MYPY_FALSE
elif results <= {ALWAYS_FALSE, MYPY_FALSE}:
return ALWAYS_FALSE
elif expr.op == "and":
if ALWAYS_FALSE in results:
return ALWAYS_FALSE
elif MYPY_FALSE in results:
return MYPY_FALSE
elif left == right == ALWAYS_TRUE:
return ALWAYS_TRUE
elif results <= {ALWAYS_TRUE, MYPY_TRUE}:
return MYPY_TRUE
return TRUTH_VALUE_UNKNOWN
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Truth-table for and/or misclassifies some MYPY_TRUE/MYPY_FALSE combinations

The overall approach of combining left/right via a small truth table is solid, but a few edge combinations are classified inconsistently with the documented semantics of the flags:

  • For or:

    • left = MYPY_TRUE, right = MYPY_FALSE (e.g. TYPE_CHECKING or not TYPE_CHECKING) should be ALWAYS_TRUE (true in both mypy and at runtime), but this logic returns MYPY_TRUE because of the elif MYPY_TRUE in results branch.
    • left = ALWAYS_FALSE, right = MYPY_FALSE (e.g. COMPILE_TIME_FALSE or not TYPE_CHECKING) should be MYPY_FALSE, but this logic returns ALWAYS_FALSE via results <= {ALWAYS_FALSE, MYPY_FALSE}.
  • For and:

    • left = MYPY_TRUE, right = MYPY_FALSE (e.g. TYPE_CHECKING and not TYPE_CHECKING) should be ALWAYS_FALSE, but is currently classified as MYPY_FALSE via elif MYPY_FALSE in results.

The and misclassification currently behaves the same as ALWAYS_FALSE in infer_reachability_of_if_statement, but the or misclassification (ALWAYS_TRUE vs MYPY_TRUE) affects whether mark_block_mypy_only is applied, so conditions like TYPE_CHECKING or not TYPE_CHECKING will be treated as mypy-only instead of always-true.

Also, the inner if expr.op not in ("or", "and") check is unreachable given the surrounding expr.op in ("and", "or") guard.

Consider tightening this truth table so the four (mypy, runtime) combinations are combined exactly (possibly via a small mapping from code → (bool, bool) and back) and removing the redundant inner guard. Adding a regression test around TYPE_CHECKING or not TYPE_CHECKING (and a COMPILE_TIME_FALSE or not TYPE_CHECKING variant) would help lock in the intended behavior.


I'll verify the claims in the review comment by examining the code context, understanding the semantics of the constants, and checking the logic.


---

🏁 Script executed:

```shell
#!/bin/bash
# Examine the function containing the problematic code and surrounding context
cat mypy/reachability.py | head -200 | tail -100

Length of output: 3758


🏁 Script executed:

#!/bin/bash
# Search for the constant definitions to understand semantics
rg -n "ALWAYS_TRUE|MYPY_TRUE|ALWAYS_FALSE|MYPY_FALSE" mypy/reachability.py | head -30

Length of output: 1370


🏁 Script executed:

#!/bin/bash
# Look at the full function to understand the outer guard
rg -n "def infer_condition_value" mypy/reachability.py -A 50

Length of output: 2196


🏁 Script executed:

#!/bin/bash
# Find where this function is called and understand the pattern
rg -n "expr\.op in.*and.*or" mypy/reachability.py -B5 -A5

Length of output: 461


🏁 Script executed:

#!/bin/bash
# Search for TYPE_CHECKING related checks to understand the use cases
rg -n "TYPE_CHECKING" mypy/reachability.py -C3

Length of output: 284


Truth-table for and/or has logic errors causing incorrect classifications

Verification confirms three truth-table misclassifications in the or/and logic:

  1. TYPE_CHECKING or not TYPE_CHECKING (MYPY_TRUE, MYPY_FALSE) returns MYPY_TRUE but should return ALWAYS_TRUE—it's true both in mypy (T or F) and at runtime (F or T).

  2. COMPILE_TIME_FALSE or not TYPE_CHECKING (ALWAYS_FALSE, MYPY_FALSE) returns ALWAYS_FALSE but should return MYPY_FALSE—it's false in mypy (F or F) but true at runtime (F or T).

  3. TYPE_CHECKING and not TYPE_CHECKING (MYPY_TRUE, MYPY_FALSE) returns MYPY_FALSE but should return ALWAYS_FALSE—it's false in both mypy (T and F) and at runtime (F and T).

The or misclassification directly impacts mark_block_mypy_only application, causing TYPE_CHECKING or not TYPE_CHECKING to be treated as mypy-only instead of always-true. The inner guard at line 131 (if expr.op not in ("or", "and")) is unreachable due to the outer condition at line 130.

Consider refactoring the truth table to systematically enumerate all four (mypy, runtime) combinations and remove the redundant inner guard. Adding regression tests for TYPE_CHECKING or not TYPE_CHECKING and COMPILE_TIME_FALSE or not TYPE_CHECKING would prevent future regressions.

@github-actions
Copy link

Diff from mypy_primer, showing the effect of this PR on open source code:

pydantic (https://github.com/pydantic/pydantic)
- pydantic/_internal/_docs_extraction.py:104: error: Incompatible types in assignment (expression has type "list[str] | None", variable has type "list[str]")  [assignment]

discord.py (https://github.com/Rapptz/discord.py)
- discord/ext/commands/hybrid.py:631: error: Overlap between argument names and ** TypedDict items: "name", "description"  [misc]
+ discord/ext/commands/hybrid.py:631: error: Overlap between argument names and ** TypedDict items: "description", "name"  [misc]
- discord/ext/commands/hybrid.py:836: error: Overlap between argument names and ** TypedDict items: "name", "with_app_command"  [misc]
+ discord/ext/commands/hybrid.py:836: error: Overlap between argument names and ** TypedDict items: "with_app_command", "name"  [misc]
- discord/ext/commands/hybrid.py:860: error: Overlap between argument names and ** TypedDict items: "name", "with_app_command"  [misc]
+ discord/ext/commands/hybrid.py:860: error: Overlap between argument names and ** TypedDict items: "with_app_command", "name"  [misc]
- discord/ext/commands/bot.py:290: error: Overlap between argument names and ** TypedDict items: "name", "with_app_command"  [misc]
+ discord/ext/commands/bot.py:290: error: Overlap between argument names and ** TypedDict items: "with_app_command", "name"  [misc]
- discord/ext/commands/bot.py:314: error: Overlap between argument names and ** TypedDict items: "name", "with_app_command"  [misc]
+ discord/ext/commands/bot.py:314: error: Overlap between argument names and ** TypedDict items: "with_app_command", "name"  [misc]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants