Skip to content

[flake8-bugbear] Flag useless subscript access in B018#23966

Draft
shivamtiwari3 wants to merge 2 commits intoastral-sh:mainfrom
shivamtiwari3:fix/b018-flag-useless-subscript-access
Draft

[flake8-bugbear] Flag useless subscript access in B018#23966
shivamtiwari3 wants to merge 2 commits intoastral-sh:mainfrom
shivamtiwari3:fix/b018-flag-useless-subscript-access

Conversation

@shivamtiwari3
Copy link
Copy Markdown

Summary

Fixes #23932.

B018 already flags useless attribute accesses (e.g. obj.attr) even when
contains_effect returns true for them, because attribute access can trigger
__getattribute__. This PR applies the same treatment to subscript expressions
(e.g. x["item"]), which contains_effect also marks as potentially
side-effectful (due to __getitem__), but which are equally "useless" when
the result is discarded.


Root Cause

contains_effect in ruff_python_ast/src/helpers.rs (line 123) unconditionally
returns true for Expr::Subscript. In B018's useless_expression function
(flake8_bugbear/rules/useless_expression.rs), when contains_effect returns
true the function exits early — unless the top-level expression is an
Attribute, in which case it still reports a diagnostic. Subscripts received no
such special-case, so x["item"] was silently skipped.

Solution

Mirror the existing attribute special-case for subscript expressions:

// Flag subscripts as useless expressions, even if they're attached to calls or other
// expressions.
if value.is_subscript_expr() {
    checker.report_diagnostic(
        UselessExpression {
            kind: Kind::Subscript,
        },
        value.range(),
    );
}

A new Kind::Subscript variant is added with the message:

Found useless subscript access. Either assign it to a variable or remove it.

Testing

Added foo6() to B018.py covering:

  • x["item"] — plain name subscript → flagged
  • obj.attr["key"] — subscript on attribute → flagged
  • get_dict()["key"] — subscript on call result → flagged
  • x[0] — integer key → flagged
  • _ = x["item"] — intentional ignore (assignment) → not flagged

All existing B018 tests (B018.py and B018.ipynb) still pass. Snapshot
updated accordingly.

Checklist

  • Fixes the root cause (not just the symptom)
  • New test covers the exact failing scenario from the issue
  • All existing tests pass (cargo test -p ruff_linter)
  • No unrelated changes
  • Code style matches project conventions
  • uvx prek run -a passes (rustfmt, clippy, ruff format/check, etc.)

@astral-sh-bot astral-sh-bot bot requested a review from ntBre March 15, 2026 08:05
@ntBre ntBre changed the title [] Flag useless subscript access in B018 [flake8-bugbear] Flag useless subscript access in B018 Mar 16, 2026
@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Mar 16, 2026

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+126 -1 violations, +0 -0 fixes in 17 projects; 39 projects unchanged)

DisnakeDev/disnake (+17 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ disnake/embeds.py:782:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/ext/commands/test_params.py:102:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/ext/commands/test_params.py:134:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/ext/commands/test_params.py:138:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/ext/commands/test_params.py:141:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/ext/commands/test_params.py:146:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/ext/commands/test_params.py:159:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/ext/commands/test_params.py:163:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/ext/commands/test_params.py:167:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/ext/commands/test_params.py:170:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
... 7 additional changes omitted for project

PlasmaPy/PlasmaPy (+9 -1 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ src/plasmapy/particles/ionization_state_collection.py:238:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ src/plasmapy/particles/particle_class.py:946:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ src/plasmapy/particles/particle_class.py:949:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/diagnostics/test_thomson.py:296:5: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/diagnostics/test_thomson.py:745:5: PLR0917 Too many positional arguments (9/5)
- tests/diagnostics/test_thomson.py:745:5: PLR0917 Too many positional arguments (9/5)
+ tests/particles/test_ionization_collection.py:624:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/particles/test_ionization_state.py:351:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
... 3 additional changes omitted for rule B018

apache/airflow (+14 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL

+ airflow-core/tests/unit/models/test_taskinstance.py:2075:21: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ airflow-core/tests/unit/models/test_taskinstance.py:2088:21: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ airflow-core/tests/unit/models/test_taskinstance.py:2090:21: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ airflow-core/tests/unit/models/test_taskinstance.py:2132:21: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ airflow-core/tests/unit/models/test_xcom_arg.py:127:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ providers/amazon/src/airflow/providers/amazon/aws/executors/ecs/ecs_executor.py:547:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ task-sdk/tests/task_sdk/definitions/test_xcom_arg.py:317:17: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ task-sdk/tests/task_sdk/definitions/test_xcom_arg.py:324:17: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ task-sdk/tests/task_sdk/execution_time/test_context.py:685:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ task-sdk/tests/task_sdk/execution_time/test_lazy_sequence.py:148:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
... 4 additional changes omitted for project

bokeh/bokeh (+7 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL

+ tests/unit/bokeh/colors/test_util__colors.py:126:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/unit/bokeh/colors/test_util__colors.py:133:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/unit/bokeh/colors/test_util__colors.py:135:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/unit/bokeh/colors/test_util__colors.py:139:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/unit/bokeh/colors/test_util__colors.py:141:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/unit/bokeh/colors/test_util__colors.py:143:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ tests/unit/bokeh/document/test_models.py:85:13: B018 Found useless subscript access. Either assign it to a variable or remove it.

freedomofpress/securedrop (+4 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ securedrop/pretty_bad_protocol/_meta.py:244:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ securedrop/tests/conftest.py:294:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ securedrop/tests/migrations/migration_b58139cfdc8c.py:197:21: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ securedrop/tests/migrations/migration_b58139cfdc8c.py:203:21: B018 Found useless subscript access. Either assign it to a variable or remove it.

ibis-project/ibis (+15 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ ibis/backends/duckdb/tests/test_client.py:167:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ ibis/backends/tests/test_api.py:76:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ ibis/common/collections.py:132:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ ibis/common/tests/test_grounds.py:355:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ ibis/common/tests/test_grounds.py:368:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ ibis/expr/operations/tests/test_core.py:212:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ ibis/expr/operations/tests/test_core.py:215:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ ibis/tests/expr/test_table.py:177:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ ibis/tests/expr/test_table.py:182:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ ibis/tests/expr/test_table.py:367:5: B018 Found useless subscript access. Either assign it to a variable or remove it.
... 5 additional changes omitted for project

prefecthq/prefect (+1 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ tests/test_settings.py:2310:13: B018 Found useless subscript access. Either assign it to a variable or remove it.

pypa/setuptools (+1 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ setuptools/command/_requirestxt.py:50:9: B018 Found useless subscript access. Either assign it to a variable or remove it.

reflex-dev/reflex (+1 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ reflex/components/component.py:2000:17: B018 Found useless subscript access. Either assign it to a variable or remove it.

zulip/zulip (+13 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL

+ zerver/lib/event_schema.py:488:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ zerver/lib/event_schema.py:508:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ zerver/lib/event_schema.py:657:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ zerver/tests/test_custom_profile_data.py:1285:17: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ zerver/tests/test_subdomains.py:94:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ zerver/tests/test_upload_s3.py:394:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ zerver/tests/test_upload_s3.py:665:9: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ zerver/tests/test_validators.py:357:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ zerver/tests/test_validators.py:359:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ zerver/tests/test_validators.py:361:13: B018 Found useless subscript access. Either assign it to a variable or remove it.
... 3 additional changes omitted for project

zanieb/huggingface-notebooks (+9 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select A,E703,F704,B015,B018,D100

+ peft_docs/en/clm-prompt-tuning.ipynb:cell 10:7:1: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ peft_docs/en/clm-prompt-tuning.ipynb:cell 8:2:1: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ peft_docs/en/pytorch/clm-prompt-tuning.ipynb:cell 10:7:1: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ peft_docs/en/pytorch/clm-prompt-tuning.ipynb:cell 8:2:1: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ peft_docs/en/pytorch/seq2seq-prefix-tuning.ipynb:cell 8:15:1: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ peft_docs/en/seq2seq-prefix-tuning.ipynb:cell 8:15:1: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ peft_docs/en/tensorflow/clm-prompt-tuning.ipynb:cell 10:7:1: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ peft_docs/en/tensorflow/clm-prompt-tuning.ipynb:cell 8:2:1: B018 Found useless subscript access. Either assign it to a variable or remove it.
+ peft_docs/en/tensorflow/seq2seq-prefix-tuning.ipynb:cell 8:15:1: B018 Found useless subscript access. Either assign it to a variable or remove it.

python-trio/trio (+1 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ src/trio/_tests/test_repl.py:306:5: B018 Found useless subscript access. Either assign it to a variable or remove it.

wntrblm/nox (+1 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ tests/test_manifest.py:116:9: B018 Found useless subscript access. Either assign it to a variable or remove it.

... Truncated remaining completed project reports due to GitHub comment length restrictions

Changes by rule (2 rules affected)

code total + violation - violation + fix - fix
B018 125 125 0 0 0
PLR0917 2 1 1 0 0

@ntBre
Copy link
Copy Markdown
Contributor

ntBre commented Mar 16, 2026

Thanks! I think we should make this a preview change since this is a significant expansion to a stable rule (as shown by the ecosystem check).

@ntBre ntBre added rule Implementing or modifying a lint rule preview Related to preview mode features labels Mar 16, 2026
@jbr-noba
Copy link
Copy Markdown

Thanks! I think we should make this a preview change since this is a significant expansion to a stable rule (as shown by the ecosystem check).

From a quick look, most ecosystem violations happen in voluntary places, either inside a try: ... except KeyError:, or in a with pytest.raises(KeyError): context. Is it possible to detect these cases and not flag them?

shivamtiwari3 and others added 2 commits April 7, 2026 02:38
Root cause: `contains_effect` returns `true` for `Expr::Subscript`
(because `__getitem__` may have side effects), causing `B018` to skip
subscript expressions entirely — just as it used to do for attributes.

Fix: mirror the attribute-access special case that was added in astral-sh#3455.
When `contains_effect` returns `true` but the top-level expression is a
subscript, report `UselessExpression` with a new `Subscript` kind and
the message "Found useless subscript access. Either assign it to a
variable or remove it."

Fixes astral-sh#23932
The subscript flagging is a significant expansion to a stable rule (123 new
violations across the ecosystem), so it should only run in preview mode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@shivamtiwari3 shivamtiwari3 force-pushed the fix/b018-flag-useless-subscript-access branch from ca5aa11 to 2ee4802 Compare April 6, 2026 21:09
@MichaReiser
Copy link
Copy Markdown
Member

This PR will benefit from the new SideEffect::Maybe that gets introduced in #24426 (comment). We should reconsider the implementation once that PR landed.

@MichaReiser MichaReiser marked this pull request as draft April 7, 2026 12:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview Related to preview mode features rule Implementing or modifying a lint rule

Projects

None yet

Development

Successfully merging this pull request may close these issues.

B018 does not trigger on dictionary subscript

4 participants