Skip to content

[ruff] Add rule for default values on method receivers (RUF077) - #26700

Merged
ntBre merged 32 commits into
astral-sh:mainfrom
RafaelJohn9:rule-method-receiver-default
Sep 9, 2026
Merged

ntBre merged 32 commits into
astral-sh:mainfrom
RafaelJohn9:rule-method-receiver-default

Conversation

@RafaelJohn9

@RafaelJohn9 RafaelJohn9 commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds a new Ruff rule that flags method receiver parameters (self / cls) when they are given a default value.

The rule targets functions defined directly inside a class body and uses semantic context to ensure it only applies to methods, not standalone functions. It determines the receiver kind, inspects the first positional parameter, and reports a diagnostic when that receiver has a default value.

Closes #25820

Test Plan

Tested by adding a Ruff fixture and snapshot for RUF077, and verifying the rule is triggered for methods whose receiver parameter has a default value.

Files added/updated for testing include:

  • crates/ruff_linter/resources/test/fixtures/ruff/RUF077.py
  • crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF077_RUF077.py.snap

I also validated the rule integration by building Ruff locally after registering the rule and hooking it into AST statement analysis.

@astral-sh-bot
astral-sh-bot Bot requested a review from ntBre July 10, 2026 12:27
@astral-sh-bot

astral-sh-bot Bot commented Jul 10, 2026 •

Copy link
Copy Markdown

ruff-ecosystem results

Linter (stable)

βœ… ecosystem check detected no linter changes.

Linter (preview)

βœ… ecosystem check detected no linter changes.

Signed-off-by: John Kagunda <rafaeljohb@gmail.com>
Signed-off-by: John Kagunda <rafaeljohb@gmail.com>
@ntBre ntBre added rule Implementing or modifying a lint rule preview Related to preview-mode features labels Jul 13, 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! This looks good overall, but we need to make sure to add the rule in preview, and I think we can reuse some existing code for the static/class/instance method checks. I also had a request about the tests.

@ntBre

ntBre commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Looking back at the issue, I also saw in Micha's comment:

We need to make sure to correctly handle @classmethod,__new__, ..., @override (any decorators?)

I think we may want to conservatively bail out in the presence of any other decorators, but especially @override, which indicates that the function was inherited and this definition doesn't control its signature.

@RafaelJohn9
RafaelJohn9 requested a review from ntBre July 13, 2026 19:21

@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.

This relates to my inline comments, but the one ecosystem hit appears to be a false positive:

The function has a decorator that we can't resolve and ends up producing a static method:

https://github.com/zmievsa/cadwyn/blob/22241f2d9d92e36d0222fd2536effb43b06e3b6e/cadwyn/structure/data.py#L209

…dated md tests

Signed-off-by: John Kagunda <rafaeljohb@gmail.com>
@RafaelJohn9
RafaelJohn9 requested a review from ntBre July 14, 2026 21:29
@RafaelJohn9
RafaelJohn9 requested a review from ntBre July 16, 2026 20:36
@RafaelJohn9

RafaelJohn9 commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor Author

hello @ntBre , We good to go? :)

@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.

Thank you! This is looking good, just a couple more edge cases to consider.

@ntBre ntBre changed the title feat: Add rule for default values on method receivers [ruff] Add rule for default values on method receivers (RUF077) Jul 31, 2026
RafaelJohn9 and others added 7 commits August 2, 2026 17:43
…t.rs

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
… it's the sole decorator

Previously receiver_kind treated any function classified as FunctionType::ClassMethod as having a class receiver, even when it carried unrelated or stacked decorators alongside @classmethod, which could change how the receiver is actually bound and produce false positives. Now the check only trusts the classification unconditionally for implicit classmethods (e.g. __init_subclass__), and for explicitly decorated ones only when @classmethod (or a configured equivalent) is the sole decorator.

This required exposing is_class_method from ruff_python_semantic::analyze::function_type as pub so it can be called directly with the single decorator, and adds a snapshot test covering the new mdtest case.
@RafaelJohn9
RafaelJohn9 requested a review from ntBre August 8, 2026 20:30

@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.

I repeat this in the inline comment, but sorry for not noticing this earlier! I didn't realize we were doing all of this classification work just to customize the error message. I think we can just make the message suitably generic and then replace all of the receiver_kind stuff with something like:

let has_receiver = match decorator_list {
    [] => true,
    [decorator] => is_class_method(
        decorator,
        semantic,
        &checker.settings().pep8_naming.classmethod_decorators,
    ),
    _ => false,
};

if !has_receiver {
    return;
}

The custom messages were a nice touch, but it's not really clear what to do with a "maybe" metaclass, which drew my attention to this, and I think the overall simplification is a better tradeoff.

@RafaelJohn9
RafaelJohn9 requested a review from ntBre August 13, 2026 08:34
RafaelJohn9 and others added 6 commits September 6, 2026 14:05
`fix_title` promised removing the default value, but no `Fix` was ever
attached to the diagnostic, so `--fix` was a no-op. Attach an unsafe
`Edit::deletion` that removes the default (and any surrounding
parentheses) while leaving an annotation on the receiver untouched, and
add mdtest coverage for the fix, including the parenthesized-default
and annotated-receiver cases.
… check

Per review feedback, replace the classify()/FunctionType dispatch with
a direct match on decorator_list: no decorators means it's always a
receiver-bearing method (including implicit classmethods like
__init_subclass__), and a single decorator only counts if it's the one
that makes the function a classmethod. This drops now-unnecessary
FunctionType/classify plumbing without changing behavior; the mdtest
suite (including the previously separate is_metaclass handling that
message-generification already made unnecessary) is unaffected.
…thod-receiver-default

# Conflicts:
#	crates/ruff_linter/src/codes.rs
RUF077 registering in the merged rule table changes the preview
rule listing this inline snapshot asserts on.

@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.

Thank you! I pushed one small change documenting the options used, but this otherwise looks good to me!

Thanks for staying on top of all the merges and even picking a category! I think suspicious is perfect.

@ntBre
ntBre merged commit 6748126 into astral-sh:main Sep 9, 2026
50 checks passed
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.

report an error when assigning a default value to self or cls

2 participants