Skip to content

docs: clarify C901 complexity example#25186

Closed
ya-nsh wants to merge 1 commit into
astral-sh:mainfrom
ya-nsh:docs/c901-clarify-complexity-example
Closed

docs: clarify C901 complexity example#25186
ya-nsh wants to merge 1 commit into
astral-sh:mainfrom
ya-nsh:docs/c901-clarify-complexity-example

Conversation

@ya-nsh

@ya-nsh ya-nsh commented May 15, 2026

Copy link
Copy Markdown

Summary

  • clarifies the C901 documentation example so the before/after guidance does not imply that guard clauses necessarily reduce McCabe complexity
  • replaces the previous nested-vs-guard-clause example with an example that moves decision-making into a focused helper

Fixes #25028.

Validation

  • git diff --check
  • sanity-checked the edited docs block with a short Python script

Note: I attempted the repository preflight command uvx prek run -a, but this environment does not have the Rust toolchain installed, so the rustfmt hook failed before running:

error: Failed to run hook `rustfmt`
  caused by: Run command `run system command` failed
  caused by: No such file or directory (os error 2)

This change only updates Rust doc comments for the generated rule documentation.

@astral-sh-bot astral-sh-bot Bot requested a review from ntBre May 15, 2026 20:08
@ntBre ntBre added the documentation Improvements or additions to documentation label May 15, 2026

@ntBre ntBre left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks! I think this is a little better, but it feels a bit outside the spirit of the rule to me just to move the complexity into a helper function (which would actually make reading/reviewing the code more complex, in my opinion). Codex suggested something like the following, where we replace a sequence of if conditions with a dict lookup:

# Before: branch-heavy lookup
def normalize_status(status):
    if status == "new":
        return "queued"
    if status == "queued":
        return "running"
    if status == "running":
        return "done"
    if status == "failed":
        return "retry"
    if status == "cancelled":
        return "closed"
    return "unknown"


# After: the decision table is data, not control flow
STATUS_TRANSITIONS = {
    "new": "queued",
    "queued": "running",
    "running": "done",
    "failed": "retry",
    "cancelled": "closed",
}


def normalize_status_lookup(status):
    return STATUS_TRANSITIONS.get(status, "unknown")

That specifically is pretty verbose, but I kind of like something in that direction.

I also realized that the default value of max-complexity is 10, so neither the existing nor new input examples actually trigger C901 out of the box. I don't think we need to extend the length of the examples to meet that level of complexity, but we should probably note that somewhere (e.g. "These examples assume a lint.mccabe.max-complexity of ..."). I'll try to remember to comment on #18972 to mark this rule as an exception, assuming we go that route.

@ya-nsh

This comment was marked as low quality.

adityasingh2400 added a commit to adityasingh2400/ruff that referenced this pull request May 21, 2026
ntBre's comment on the prior revision was right that the techniques
paragraph just paraphrased "to reduce complexity, reduce complexity"
without showing what an actual reduction looks like. Replaced the
nested if/else example with the Before/After dict-lookup pair from
your suggestion on astral-sh#25186, restoring the Use instead structure with
an example that actually moves decision points out of control flow
and into data.

The Before has 5 top-level if statements so the function's McCabe
complexity is 6 (one for the function plus one per if). The After is
1, so the rewrite is a real reduction, not a wash. Added a note that
both examples sit under the default lint.mccabe.max-complexity of 10
and would need max-complexity = 5 or less to actually trip C901.

Kept the single sentence about guard clauses not lowering complexity
on their own, since that is still the load-bearing point for the
issue this PR closes (astral-sh#25028).
ntBre added a commit that referenced this pull request May 21, 2026
The C901 docstring currently pairs a 3-deep `if/else` example with a
guard-clause rewrite under "Use instead". Both versions have McCabe
complexity 4, so the rewrite trips (or passes) C901 identically and the
docs end up teaching the false equivalence "guard clauses lower McCabe
complexity." A reader filed #25028 to flag this.

In that thread, MichaReiser said the docs should either use an example
that actually reduces complexity or drop the "Use instead" block
altogether. A prior attempt (#25137) was closed with the same comment,
and a follow-up trying a lookup-table example (#25186) was also closed,
which makes me think the safer option is the second one MichaReiser
offered. I dropped the "Use instead" block and added a short paragraph
naming the techniques that genuinely reduce McCabe complexity
(extracting helpers, lookup tables, lower branching factor), with an
explicit note that mechanical guard-clause inversion does not.

Docstring-only. `cargo dev generate-all` and `uvx prek run --from-ref
main` both pass.

closes #25028

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
thejchap pushed a commit to thejchap/ruff that referenced this pull request May 23, 2026
The C901 docstring currently pairs a 3-deep `if/else` example with a
guard-clause rewrite under "Use instead". Both versions have McCabe
complexity 4, so the rewrite trips (or passes) C901 identically and the
docs end up teaching the false equivalence "guard clauses lower McCabe
complexity." A reader filed astral-sh#25028 to flag this.

In that thread, MichaReiser said the docs should either use an example
that actually reduces complexity or drop the "Use instead" block
altogether. A prior attempt (astral-sh#25137) was closed with the same comment,
and a follow-up trying a lookup-table example (astral-sh#25186) was also closed,
which makes me think the safer option is the second one MichaReiser
offered. I dropped the "Use instead" block and added a short paragraph
naming the techniques that genuinely reduce McCabe complexity
(extracting helpers, lookup tables, lower branching factor), with an
explicit note that mechanical guard-clause inversion does not.

Docstring-only. `cargo dev generate-all` and `uvx prek run --from-ref
main` both pass.

closes astral-sh#25028

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
anishgirianish pushed a commit to anishgirianish/ruff that referenced this pull request May 28, 2026
The C901 docstring currently pairs a 3-deep `if/else` example with a
guard-clause rewrite under "Use instead". Both versions have McCabe
complexity 4, so the rewrite trips (or passes) C901 identically and the
docs end up teaching the false equivalence "guard clauses lower McCabe
complexity." A reader filed astral-sh#25028 to flag this.

In that thread, MichaReiser said the docs should either use an example
that actually reduces complexity or drop the "Use instead" block
altogether. A prior attempt (astral-sh#25137) was closed with the same comment,
and a follow-up trying a lookup-table example (astral-sh#25186) was also closed,
which makes me think the safer option is the second one MichaReiser
offered. I dropped the "Use instead" block and added a short paragraph
naming the techniques that genuinely reduce McCabe complexity
(extracting helpers, lookup tables, lower branching factor), with an
explicit note that mechanical guard-clause inversion does not.

Docstring-only. `cargo dev generate-all` and `uvx prek run --from-ref
main` both pass.

closes astral-sh#25028

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

C901 complex-structure has unclear documentation

3 participants