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

new lint: recursive_format_impl #8188

Merged
merged 1 commit into from
Feb 16, 2022

Conversation

jamesmcm
Copy link
Contributor

@jamesmcm jamesmcm commented Dec 28, 2021

The to_string_in_display lint is renamed to recursive_format_impl
A check is added for the use of self formatted with Display or Debug inside any format string in the same impl
The to_string_in_display check is kept as is - like in the format_in_format_args lint

This is my first contribution so please check it for better / more idiomatic checks + error messages. Note the format macro paths are shared with the format_in_format_args lint - maybe those could be moved to clippy utils too.

This relates to issues #2691 and #7830


changelog: Renamed to_string_in_display lint to [recursive_format_impl] with new check for any use of self as Display or Debug inside the same format trait impl.

@rust-highfive
Copy link

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @giraffate (or someone else) soon.

Please see the contribution instructions for more information.

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Dec 28, 2021
@jamesmcm jamesmcm force-pushed the recursive_display_impl branch 3 times, most recently from 147cb29 to a0ce3b0 Compare December 28, 2021 19:04
@ghost
Copy link

ghost commented Dec 29, 2021

It seems like the lint doesn't handle nested structures correctly.

Here is a false positive:

#![allow(dead_code)]
#![warn(clippy::all)]

use std::fmt;

enum Tree {
    Leaf,
    Node(Vec<Tree>),
}

impl fmt::Display for Tree {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Tree::Leaf => write!(f, "*"),
            Tree::Node(children) => {
                write!(f, "(")?;
                for child in children.iter() {
                    write!(f, "{},", child)?;
                }
                write!(f, ")")
            }
        }
    }
}

fn main() {}

warning: using `self` in `fmt::Display` implementation might lead to infinite recursion
  --> src/main.rs:18:21
   |
18 |                     write!(f, "{},", child)?;
   |                     ^^^^^^^^^^^^^^^^^^^^^^^
   |
note: the lint level is defined here
  --> src/main.rs:2:9
   |
2  | #![warn(clippy::all)]
   |         ^^^^^^^^^^^
   = note: `#[warn(clippy::recursive_display_impl)]` implied by `#[warn(clippy::all)]`
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#recursive_display_impl
   = note: this warning originates in the macro `$crate::format_args` (in Nightly builds, run with -Z macro-backtrace for more info)

@jamesmcm
Copy link
Contributor Author

jamesmcm commented Dec 29, 2021

Thanks, the issue is when the nested type is actually the same as the Self type. It seems the best thing to do would be to limit the lint to check whether it is actually the same self reference (not just the types).

I'll add it as a test case and try to work around it, thanks!

@jamesmcm
Copy link
Contributor Author

Fixed by dealing with the single deref of &self to self inside fmt() as a special case, instead of just checking the types match (that caused the false positive above).

@camsteffen
Copy link
Contributor

camsteffen commented Jan 11, 2022

I realized with #8253 that this lint should work for any formatting trait (e.g. Debug). The two lints can share a LintPass and a naming convention. For the common part of the name I'm thinking either "fmt" or "format_impl".

@jamesmcm
Copy link
Contributor Author

It's a good idea @camsteffen but note the implementations would differ a fair bit since atm this uses:
https://github.com/rust-lang/rust-clippy/pull/8188/files#diff-37e08397df0148cab790ac270ccda86159451eb034ef01a7b59bc46b00074ca4R168-R174

                    if !arg.is_display() {
                        continue;
                    }

to filter the format args, unfortunately there isn't an equivalent for debug atm. See the source code here - https://doc.rust-lang.org/nightly/nightly-rustc/src/clippy_utils/higher.rs.html#680-688

Also note that it's okay to use Debug in the Display Impl and vice versa.

I'll try to look at adding the above for Debug when I get some time free.

@camsteffen
Copy link
Contributor

It would be perfectly fine to leave other traits as a future enhancement. I was just thinking about the lint name since we like to get that right the first time.

@jamesmcm jamesmcm force-pushed the recursive_display_impl branch 2 times, most recently from da2cd64 to 6e97fc9 Compare January 14, 2022 14:16
@jamesmcm jamesmcm changed the title new lint: recursive_display_impl new lint: recursive_format_trait_impl Jan 14, 2022
@jamesmcm
Copy link
Contributor Author

@camsteffen renamed to recursive_format_trait_impl and rebased on upstream (there were some changes to the FormatArg macro parsing).

atm I only check for Debug and Display, but one could add the others (Binary, etc.) pretty easily. It also doesn't check the case where there's a reference cycle i.e. Debug calls Display, and Display calls Debug - we could detect this if we store more information about the other format trait impls as we do the pass, but it doesn't seem like something that is likely to come up in real code anyway.

@camsteffen
Copy link
Contributor

I think recursive_format_impl would be clear enough and shorter?

@jamesmcm jamesmcm changed the title new lint: recursive_format_trait_impl new lint: recursive_format_impl Jan 17, 2022
@giraffate
Copy link
Contributor

@camsteffen You've already reviewed some of this, so can you review it as is?

@camsteffen
Copy link
Contributor

Okay.

r? @camsteffen

@camsteffen camsteffen assigned camsteffen and unassigned giraffate Jan 19, 2022
Copy link
Contributor

@camsteffen camsteffen left a comment

Choose a reason for hiding this comment

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

This is looking good! Nice tests. I have some comments but it's pretty minor stuff.

It would be nice to have a separate commit to rename the lint. Also we need to add a register_renamed call and add to the rename.rs test.

&paths::PRINTLN_MACRO,
&paths::WRITE_MACRO,
&paths::WRITELN_MACRO,
];
Copy link
Contributor

Choose a reason for hiding this comment

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

We should avoid duplicating these lists. Instead we can have a util like is_format_macro(DefId) -> bool.

But I'm also thinking it might be a good idea to just use the FormatArgs LintPass. Up to you.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, but I'll leave making the changes to the FormatArgs lint to another PR so as not to make this huge.

How could we re-use the lint pass, when here we need to check we are inside the impl Display or Debug, etc. ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Done, but I'll leave making the changes to the FormatArgs lint to another PR so as not to make this huge.

I still would like to avoid duplicating the list in this PR. I small refactor can be done in the other lint.

How could we re-use the lint pass, when here we need to check we are inside the impl Display or Debug, etc. ?

It seems like it should be possible to merge the two lint passes and maintain all cases. But I may be underestimating the difficulty. Again I'll leave it up to you to decide if that ultimately makes sense to do.

Copy link
Contributor

Choose a reason for hiding this comment

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

@jamesmcm did you decide against merging the lint passes or still looking into it?

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 took a look at it, in theory it should be possible, it's not trivial though since the format_args case also needs to work when the macro isn't nested. It might be best to do in another PR though.

Btw is there any way to share the pass without combining the source files (and error files)?

Copy link
Contributor

Choose a reason for hiding this comment

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

If we combine them, I think it would be better to do it in this PR since it would be less churn and reviewing. The tests can still be separated per lint.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cool, looking at it in detail I don't think it is worth combining them. Since handling the nested macro difference means the code isn't really shared between the non format trait impl and format trait impl cases, so it's more just moving it around and I don't think that's worth merging them (since at that point you might as well merge #8253 too for example).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@camsteffen considering this, I think this is mergeable as is?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry for the delay.

(since at that point you might as well merge #8253 too for example).

I am planning to have that lint merged with this one at least. But if that doesn't change your decision, yes this is ready to merge!

clippy_lints/src/recursive_format_impl.rs Outdated Show resolved Hide resolved
clippy_lints/src/recursive_format_impl.rs Outdated Show resolved Hide resolved
clippy_lints/src/recursive_format_impl.rs Outdated Show resolved Hide resolved
clippy_lints/src/recursive_format_impl.rs Outdated Show resolved Hide resolved
clippy_lints/src/recursive_format_impl.rs Outdated Show resolved Hide resolved
clippy_lints/src/recursive_format_impl.rs Outdated Show resolved Hide resolved
clippy_lints/src/recursive_format_impl.rs Outdated Show resolved Hide resolved
clippy_lints/src/recursive_format_impl.rs Outdated Show resolved Hide resolved
clippy_lints/src/recursive_format_impl.rs Outdated Show resolved Hide resolved
@jamesmcm jamesmcm force-pushed the recursive_display_impl branch 2 times, most recently from 7de473e to cfb32b3 Compare January 23, 2022 20:40
@bors
Copy link
Collaborator

bors commented Jan 27, 2022

☔ The latest upstream changes (presumably #8359) made this pull request unmergeable. Please resolve the merge conflicts.

@bors
Copy link
Collaborator

bors commented Feb 6, 2022

☔ The latest upstream changes (presumably #8398) made this pull request unmergeable. Please resolve the merge conflicts.

@bors
Copy link
Collaborator

bors commented Feb 12, 2022

☔ The latest upstream changes (presumably #8418) made this pull request unmergeable. Please resolve the merge conflicts.

@bors
Copy link
Collaborator

bors commented Feb 14, 2022

☔ The latest upstream changes (presumably #8425) made this pull request unmergeable. Please resolve the merge conflicts.

The to_string_in_display lint is renamed to recursive_format_impl
A check is added for the use of self formatted with Display or Debug
inside any format string in the same impl
The to_string_in_display check is kept as is - like in the
format_in_format_args lint

For now only Display and Debug are checked
This could also be extended to other Format traits (Binary, etc.)
@camsteffen
Copy link
Contributor

Thanks for your patience, and great first contribution!

@bors r+

@bors
Copy link
Collaborator

bors commented Feb 16, 2022

📌 Commit b162b11 has been approved by camsteffen

@bors
Copy link
Collaborator

bors commented Feb 16, 2022

⌛ Testing commit b162b11 with merge b34cd79...

@bors
Copy link
Collaborator

bors commented Feb 16, 2022

☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test
Approved by: camsteffen
Pushing b34cd79 to master...

@bors bors merged commit b34cd79 into rust-lang:master Feb 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants