Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop Violation::placeholder #1996

Merged
merged 6 commits into from
Jan 19, 2023

Conversation

not-my-profile
Copy link
Contributor

@not-my-profile not-my-profile commented Jan 19, 2023

This PR removes the following associated function from the Violation trait:

fn placeholder() -> Self;

ruff previously used this placeholder approach for the messages it
listed in the README and displayed when invoked with --explain <code>.

This approach is suboptimal for three reasons:

  1. The placeholder implementations are completely boring code since they
    just initialize the struct with some dummy values.

  2. Displaying concrete error messages with arbitrary interpolated values
    can be confusing for the user since they might not recognize that the
    values are interpolated.

  3. Some violations have varying format strings depending on the
    violation which could not be documented with the previous approach
    (while we could have changed the signature to return Vec this
    would still very much suffer from the previous two points).

We therefore drop Violation::placeholder in favor of a new macro-based
approach, explained in commit 4/5.

For the details please refer to the individual commit messages.

@not-my-profile not-my-profile force-pushed the drop-placeholder branch 5 times, most recently from 774e3e2 to ba68ce8 Compare January 19, 2023 14:58
let fix_token = match rule.autofixable() {
Autofixability::Never => "",
Autofixability::Sometimes => "🛠 (sometimes)",
Autofixability::Always => "🛠",
Copy link
Member

Choose a reason for hiding this comment

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

I'd prefer to leave this for now. We should make it clearer eventually, but I worry that as-is it'll hurt the readability and simplicity of the table.

I'm happy to play around with some alternate formats in a future PR.

Screen Shot 2023-01-19 at 10 26 25 AM

Copy link
Member

Choose a reason for hiding this comment

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

(Unrelatedly: the use of the actual format strings is not only simpler for maintenance but so much clearer for users. Nice job.)

Copy link
Contributor Author

@not-my-profile not-my-profile Jan 19, 2023

Choose a reason for hiding this comment

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

Fair enough I have removed the display of "(sometimes)".

@@ -1,3 +1,23 @@
//! Checks for unused loop variables.
Copy link
Member

Choose a reason for hiding this comment

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

Should these include the rule code at the end, like you did in the DTZ examples?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think so. I think my goal is to centrally define all rule codes within a single file (including many to many mappings) and have our documentation generator combine these doc comments with the codes from that file.

I just included the codes in the datetimez comments since they were there previously ... but I don't think it really matters there since I think the datetimez rule implementations should ultimately be replaced by #1507.

(I still have to think about how to best implement the documentation generation from such doc comments ... but that's out of the scope of this PR.)

Copy link
Member

Choose a reason for hiding this comment

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

I mostly find those comments useful for grep. E.g., if someone reports a bug in SIM109, it's useful to be able to search /// SIM109 and jump to the definition. I think there's some value in making that easy, since even if we move away from those codes internally, they'll still pop up in issue reports and such. (It doesn't matter to me that they're in the rustdoc or not.)

Copy link
Contributor Author

@not-my-profile not-my-profile Jan 19, 2023

Choose a reason for hiding this comment

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

Right I think we could end up having a cargo dev jump SIM109 command that would print where the respective rule is defined (if we end up no longer defining them next to the functions).

It doesn't matter to me that they're in the rustdoc or not.

The plan is that such doc comments will become the user documentation.

quote! {
#func
fn message_formats() -> &'static [&'static str] {
&[#strings]
Copy link
Member

Choose a reason for hiding this comment

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

What's the failure mode in the event that a violation doesn't contain a format string?

Copy link
Contributor Author

@not-my-profile not-my-profile Jan 19, 2023

Choose a reason for hiding this comment

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

Compilation fails with a nice error message:

error: expected last expression to be a format! macro or a match block
   --> src/violations.rs:539:9
    |
539 |         "foo".to_string()
    |         ^^^^^

Copy link
Member

Choose a reason for hiding this comment

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

Perfect.

This commit series removes the following associated
function from the Violation trait:

    fn placeholder() -> Self;

ruff previously used this placeholder approach for the messages it
listed in the README and displayed when invoked with --explain <code>.

This approach is suboptimal for three reasons:

1. The placeholder implementations are completely boring code since they
   just initialize the struct with some dummy values.

2. Displaying concrete error messages with arbitrary interpolated values
   can be confusing for the user since they might not recognize that the
   values are interpolated.

3. Some violations have varying format strings depending on the
   violation which could not be documented with the previous approach
   (while we could have changed the signature to return Vec<Self> this
   would still very much suffer from the previous two points).

We therefore drop Violation::placeholder in favor of a new macro-based
approach, explained in commit 4/5.

Violation::placeholder is only invoked via Rule::kind, so we firstly
have to get rid of all Rule::kind invocations ... this commit starts
removing the trivial cases.
While ruff displays the string returned by Violation::message in its
output for detected violations the messages displayed in the README
and in the `--explain <code>` output previously used the
DiagnosticKind::summary() function which for some verbose messages
provided shorter descriptions.

This commit removes DiagnosticKind::summary, and moves the more
extensive documentation into doc comments ... these are not displayed
yet to the user but doing that is very much planned.
ruff_dev::generate_rules_table previously documented which rules are
autofixable via DiagnosticKind::fixable ... since the DiagnosticKind was
obtained via Rule::kind (and Violation::placeholder) which we both want
to get rid of we have to obtain the autofixability via another way.

This commit implements such another way by adding an AUTOFIX
associated constant to the Violation trait. The constant is of the type
Option<AutoFixkind>, AutofixKind is a new struct containing an
Availability enum { Sometimes, Always}, letting us additionally document
that some autofixes are only available sometimes (which previously
wasn't documented). We intentionally introduce this information in a
struct so that we can easily introduce further autofix metadata in the
future such as autofix applicability[1].

[1]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_errors/enum.Applicability.html
The idea is nice and simple we replace:

    fn placeholder() -> Self;

with

    fn message_formats() -> &'static [&'static str];

So e.g. if a Violation implementation defines:

    fn message(&self) -> String {
        format!("Local variable `{name}` is assigned to but never used")
    }

it would also have to define:

   fn message_formats() -> &'static [&'static str] {
       &["Local variable `{name}` is assigned to but never used"]
   }

Since we however obviously do not want to duplicate all of our format
strings we simply introduce a new procedural macro attribute
#[derive_message_formats] that can be added to the message method
declaration in order to automatically derive the message_formats
implementation.

This commit implements the macro. The following and final commit
updates violations.rs to use the macro. (The changes have been separated
because the next commit is autogenerated via a Python script.)
# This commit has been generated via the following Python script:
# (followed by `cargo +nightly fmt` and `cargo dev generate-all`)
# For the reasoning see the previous commit(s).

import re
import sys

for path in (
    'src/violations.rs',
    'src/rules/flake8_tidy_imports/banned_api.rs',
    'src/rules/flake8_tidy_imports/relative_imports.rs',
):
    with open(path) as f:
        text = ''

        while line := next(f, None):

            if line.strip() != 'fn message(&self) -> String {':
                text += line
                continue

            text += '    #[derive_message_formats]\n' + line

            body = next(f)
            while (line := next(f)) != '    }\n':
                body += line

            # body = re.sub(r'(?<!code\| |\.push\()format!', 'format!', body)
            body = re.sub(
                r'("[^"]+")\s*\.to_string\(\)', r'format!(\1)', body, re.DOTALL
            )
            body = re.sub(
                r'(r#".+?"#)\s*\.to_string\(\)', r'format!(\1)', body, re.DOTALL
            )

            text += body + '    }\n'

            while (line := next(f)).strip() != 'fn placeholder() -> Self {':
                text += line
            while (line := next(f)) != '    }\n':
                pass

    with open(path, 'w') as f:
        f.write(text)
@not-my-profile
Copy link
Contributor Author

not-my-profile commented Jan 19, 2023

I have just changed the associated constant introduced in the 3rd commit from:

const AUTOFIXABLE: Autofixability = Autofixability::Never;

to

const AUTOFIX: Option<AutofixKind> = None;

so that we can easily introduce another field for applicability (#1997) in the future :)

@charliermarsh charliermarsh merged commit 3c3da8a into astral-sh:main Jan 19, 2023
@charliermarsh
Copy link
Member

You're doing so much good work on pushing these abstractions forward. These are highly complex changes and you're handling them expertly. It's much appreciated.

renovate bot referenced this pull request in ixm-one/pytest-cmake-presets Jan 20, 2023
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [ruff](https://togithub.com/charliermarsh/ruff) | `^0.0.226` ->
`^0.0.227` |
[![age](https://badges.renovateapi.com/packages/pypi/ruff/0.0.227/age-slim)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://badges.renovateapi.com/packages/pypi/ruff/0.0.227/adoption-slim)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://badges.renovateapi.com/packages/pypi/ruff/0.0.227/compatibility-slim/0.0.226)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://badges.renovateapi.com/packages/pypi/ruff/0.0.227/confidence-slim/0.0.226)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>charliermarsh/ruff</summary>

###
[`v0.0.227`](https://togithub.com/charliermarsh/ruff/releases/tag/v0.0.227)

[Compare
Source](https://togithub.com/charliermarsh/ruff/compare/v0.0.226...v0.0.227)

#### What's Changed

- Drop `RuleCode` in favor of `Rule` enum with human-friendly names by
[@&#8203;not-my-profile](https://togithub.com/not-my-profile) in
[https://github.com/charliermarsh/ruff/pull/1941](https://togithub.com/charliermarsh/ruff/pull/1941)
- Make define_rule_mapping! set rule code as doc comment of variants by
[@&#8203;not-my-profile](https://togithub.com/not-my-profile) in
[https://github.com/charliermarsh/ruff/pull/1991](https://togithub.com/charliermarsh/ruff/pull/1991)
- Added pylint formatter by
[@&#8203;damienallen](https://togithub.com/damienallen) in
[https://github.com/charliermarsh/ruff/pull/1995](https://togithub.com/charliermarsh/ruff/pull/1995)
- Preserve unmatched comparators in `SIM109` by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[https://github.com/charliermarsh/ruff/pull/1998](https://togithub.com/charliermarsh/ruff/pull/1998)
- Drop `Violation::placeholder` by
[@&#8203;not-my-profile](https://togithub.com/not-my-profile) in
[https://github.com/charliermarsh/ruff/pull/1996](https://togithub.com/charliermarsh/ruff/pull/1996)
- Apply #\[derive(Default)] fixes suggested by Clippy by
[@&#8203;akx](https://togithub.com/akx) in
[https://github.com/charliermarsh/ruff/pull/2000](https://togithub.com/charliermarsh/ruff/pull/2000)
- Avoid `SIM201` and `SIM202` errors in `__ne__` et al by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[https://github.com/charliermarsh/ruff/pull/2001](https://togithub.com/charliermarsh/ruff/pull/2001)
- Fix that --explain panics by
[@&#8203;not-my-profile](https://togithub.com/not-my-profile) in
[https://github.com/charliermarsh/ruff/pull/2002](https://togithub.com/charliermarsh/ruff/pull/2002)
- Split up pydocstyle rules by [@&#8203;akx](https://togithub.com/akx)
in
[https://github.com/charliermarsh/ruff/pull/2003](https://togithub.com/charliermarsh/ruff/pull/2003)
- Add RUF005 "unpack instead of concatenating" check by
[@&#8203;akx](https://togithub.com/akx) in
[https://github.com/charliermarsh/ruff/pull/1957](https://togithub.com/charliermarsh/ruff/pull/1957)
- Enable autofix for `FitsOnOneLine` (`D200`) by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[https://github.com/charliermarsh/ruff/pull/2006](https://togithub.com/charliermarsh/ruff/pull/2006)
- Change AsRef<str> impl for Rule to kebab-case by
[@&#8203;not-my-profile](https://togithub.com/not-my-profile) in
[https://github.com/charliermarsh/ruff/pull/2009](https://togithub.com/charliermarsh/ruff/pull/2009)
- Upgrade RustPython by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[https://github.com/charliermarsh/ruff/pull/2011](https://togithub.com/charliermarsh/ruff/pull/2011)
- Avoid SIM401 in `elif` blocks by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[https://github.com/charliermarsh/ruff/pull/2012](https://togithub.com/charliermarsh/ruff/pull/2012)
- Improve --explain output by
[@&#8203;not-my-profile](https://togithub.com/not-my-profile) in
[https://github.com/charliermarsh/ruff/pull/2010](https://togithub.com/charliermarsh/ruff/pull/2010)
- Avoid checking row types for single-name
[@&#8203;parametrize](https://togithub.com/parametrize) decorators by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[https://github.com/charliermarsh/ruff/pull/2013](https://togithub.com/charliermarsh/ruff/pull/2013)

#### New Contributors

- [@&#8203;damienallen](https://togithub.com/damienallen) made their
first contribution in
[https://github.com/charliermarsh/ruff/pull/1995](https://togithub.com/charliermarsh/ruff/pull/1995)

**Full Changelog**:
astral-sh/ruff@v0.0.226...v0.0.227

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://app.renovatebot.com/dashboard#github/ixm-one/pytest-cmake-presets).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC4xMDUuNCIsInVwZGF0ZWRJblZlciI6IjM0LjEwNS40In0=-->

Signed-off-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
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.

2 participants