Skip to content

Fix false positive reportUnnecessaryComparison for bytes vs bytearray#11434

Open
behlole wants to merge 3 commits into
microsoft:mainfrom
behlole:fix/bytes-bytearray-comparable
Open

Fix false positive reportUnnecessaryComparison for bytes vs bytearray#11434
behlole wants to merge 3 commits into
microsoft:mainfrom
behlole:fix/bytes-bytearray-comparable

Conversation

@behlole
Copy link
Copy Markdown

@behlole behlole commented May 11, 2026

Fixes #11433.

Context

isTypeComparable in typeEvaluator.ts has an early return false for the "both operands are disjoint built-in classes" case. That branch already special-cases bool / int (because bool literal values can collide with 0 / 1), but bytes and bytearray fall through unhandled, even though their __eq__ implementations cross-compare by content. This produces a false-positive reportUnnecessaryComparison for very common code such as:

data = bytearray(4)
if data == b"\x00\x00\x00\x00":
    ...

Change

Add a bytes / bytearray comparable case in isTypeComparable parallel to the existing bool / int handling. If both operands are bytes-like built-ins, treat them as comparable. The Literal[bytes] form in the issue is handled by the same code path because a Literal[bytes] value is still a bytes instance (ClassType.isBuiltIn(t, 'bytes') is true for it).

Test plan

New sample packages/pyright-internal/src/tests/samples/comparison3.py covers:

  • bytesbytearray in both directions, with == and != — must NOT emit the diagnostic (validates the new branch).
  • bytearrayLiteral[bytes] — must NOT emit (validates the literal form from the issue).
  • bytesstr — must still emit (regression guard: the new branch must not over-relax).

Comparison3 test in typeEvaluator6.test.ts runs the sample twice (default rule set off, then with reportUnnecessaryComparison = 'error') and asserts the expected diagnostic count (0, then 1).

Notes

Considered also adding memoryview to the bytes-like set, since memoryview(b"a") == b"a" is also True at runtime, but kept it out of this PR to match the issue's scope exactly. Happy to add it in a follow-up if maintainers prefer.

behlole added 3 commits May 11, 2026 20:22
isTypeComparable's disjoint-built-in branch already special-cases bool/int
because their literal values can collide. Add a parallel case for bytes/bytearray:
they are distinct built-in classes, but bytes.__eq__ and bytearray.__eq__
cross-compare by content, so expressions like `b"abc" == bytearray(b"abc")` are
True at runtime. The Literal[bytes] form (e.g. `bytearray == b"..."`) is
handled by the same branch because the literal is still a bytes instance.

Fixes microsoft#11433.
Covers:
- bytes <-> bytearray in both directions, with == and != (no diagnostic)
- bytearray <-> Literal[bytes] (no diagnostic)
- bytes <-> str regression guard (still produces a diagnostic)

Refs microsoft#11433.
Runs comparison3.py twice (default rule set, then with
reportUnnecessaryComparison = 'error') and asserts diagnostic counts
of 0 then 1 — the bytes/bytearray cross-comparisons must NOT fire the
diagnostic, while the bytes-vs-str regression guard still must.

Refs microsoft#11433.
@behlole
Copy link
Copy Markdown
Author

behlole commented May 11, 2026

@microsoft-github-policy-service agree

@behlole behlole changed the title Fix/bytes bytearray comparable Fix false positive reportUnnecessaryComparison for bytes vs bytearray May 11, 2026
Copy link
Copy Markdown
Collaborator

@rchiodo rchiodo left a comment

Choose a reason for hiding this comment

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

Approved via Review Center.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

False positive: Condition will always evaluate to False since the types "bytearray" and "bytes" have no overlap

2 participants