-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Lint for using hand-writing a fold with the same behaviour as any #2350
Conversation
…tial fold value is false. Remove some unwraps
clippy_lints/src/methods.rs
Outdated
@@ -653,7 +670,8 @@ impl LintPass for Pass { | |||
GET_UNWRAP, | |||
STRING_EXTEND_CHARS, | |||
ITER_CLONED_COLLECT, | |||
USELESS_ASREF | |||
USELESS_ASREF, | |||
FOLD_ANY |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Does this macro support trailing commas? If so, please add one and the next diff is only one line :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears not:
error: unexpected end of macro invocation
--> clippy_lints/src/methods.rs:674:21
|
674 | FOLD_ANY,
| ^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh, that's too bad. Someone should make a pull request to change this :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've created an issue #2352. I'll do this next weekend if it's still open.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, this is a macro defined in the compiler, so that issue is probably more useful over at https://github.com/rust-lang/rust :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nuts. rust-lang/rust#47428
clippy_lints/src/methods.rs
Outdated
@@ -1105,6 +1125,50 @@ fn lint_iter_cloned_collect(cx: &LateContext, expr: &hir::Expr, iter_args: &[hir | |||
} | |||
} | |||
|
|||
fn lint_fold_any(cx: &LateContext, expr: &hir::Expr, fold_args: &[hir::Expr]) { | |||
// DONOTMERGE: What if this is just some other method called fold? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if applicable in this context but other parts of clippy use match_trait_method(cx, expr, &paths::ITERATOR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I tried this (well, actually, I implemented the equivalent by hand as I missed this method), but this doesn't quite do what I want - it's not enough to know that the caller is an Iterator - this could be an inherent method rather than the trait method. Maybe this is rare/ill-advised enough that it doesn't matter?
Edit: actually, I think I misread and this does do what I want after all.
clippy_lints/src/methods.rs
Outdated
expr.span, | ||
// TODO: don't suggest .any(|x| f(x)) if we can suggest .any(f) | ||
&format!( | ||
".fold(false, |{f}, {s}| {f} || {r})) is more succinctly expressed as .any(|{s}| {r})", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use span_lint_and_sugg
here to get output like this:
error: This `.fold` can be more succinctly expressed as `.any`
--> $DIR/methods.rs:390:13
|
390 | let _ = (0..3).fold(false, |acc, x| acc || x > 2);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
|
= note: `-D fold-any` implied by `-D warnings`
If you make sure the spans are right and exactly cover the .fold(…)
, this lint should work great with rustfix :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I'll just need to work out what it does first! (There's no doc comment at the moment)
Edit: no need to elaborate - I've got this working now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, Rust's diagnostic messages (aka "fancy errors") allow you to add annotations to parts of the code. One special style of annotation is a "replacement suggestion". It looks just like the "help:" thing I posted above to a human, but rustfix can parse it to automatically fix your code.
The mystical span_lint_and_sugg
is a shortcut to create a new lint message and at the same time also add a suggestion. It's basically the same as span_lint
, but you pass two parameters, help: &str
, which is the "try: " in the example above, and sugg: String
, which is the suggested replacement (something like format!(".any(|{s}| {r})", …)
in this case here).
Edit: too late :)
Edit 2: Feel free to add the above as doc comment ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks anyway. I'll add a doc comment.
I've actually not quite got this working, as I've got a span pointing at the whole expression, including the range. What's the best way of restricting this to the .fold(...)
part?
tests/ui/methods.rs
Outdated
/// Checks implementation of the `FOLD_ANY` lint | ||
fn fold_any_ignore_initial_value_of_true() { | ||
let _ = (0..3).fold(true, |acc, x| acc || x > 2); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might also want to add another test that doesn't trigger the lint because it's not using a boolean, e.g. .fold(0, |acc, x| acc += if x > 2 { 1 } else { 0 })
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
@killercup I've addressed all of your comments so far, except for reducing the span to only include |
Not sure how to best do this, but I'm sure someone else can help us out :)
Yeah, rustdoc is switching to the CommonMark standard soon. What's the errors telling you? I assume something about the |
I think that this should be extended to all of the similar methods on I don't mind just starting with |
I agree that this should be extended to cover your other examples - I was planning on merging this case first and then adding the rest. I'm happy to change the name. Not sure if we want one lint covering all cases or one lint for each case. |
I've added tests locally to check that |
@oli-obk wouldn't this apply to the method-chain lints as well? |
Jup, the method chain lints don't work on universal method calls, just on actual method calls with dot notation |
clippy_lints/src/methods.rs
Outdated
cx, | ||
FOLD_ANY, | ||
expr.span, | ||
// TODO: don't suggest .any(|x| f(x)) if we can suggest .any(f) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine, we do have another lint for that ;)
If you really want to, you can extract the code from there into a separate function so you can call it from here and there and not duplicate any logic.
clippy_lints/src/methods.rs
Outdated
span_lint_and_sugg( | ||
cx, | ||
FOLD_ANY, | ||
expr.span, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can probably use fold_args[0].span.next_point().with_hi(fold_args[1].span.hi())
to produce the span you want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works (with small tweaks). Thanks!
Thanks for the tips @oli-obk - I'll finish this PR off this weekend. |
PatKind::Ref(ref subpat, _) => get_arg_name(subpat), | ||
_ => None, | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: missing newline
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, thanks
Please don't merge just yet - I've decided to rename to unnecessary_fold and handle the other cases as part of this PR (I'm not going to worry about producing eta-reducible replacement suggestions for now). |
@oli-obk I think this is now in a mergeable state |
heads up: you'll most likely need to rebase on master to make this compile on the latest nightly |
Do you know which of the AppVeyor jobs have any hope of succeeding? The first couple of failures appear to have nothing to do with my changes. |
/// ```rust | ||
/// let _ = (0..3).fold(false, |acc, x| acc || x > 2); | ||
/// ``` | ||
/// This could be written as: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure it's good style to put an empty line above and below code blocks in markdown.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The existing lints in this file don't, for whatever that's worth
} | ||
|
||
assert!(fold_args.len() == 3, | ||
"Expected fold_args to have three entries - the receiver, the initial value and the closure"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this actually panic? I imagine a debug!
and return;
suffices
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, it's a logic bug, so I would have thought it should panic. I don't feel strongly though - I can change if it you'd prefer.
clippy_lints/src/methods.rs
Outdated
".{replacement}(|{s}| {r})", | ||
replacement = replacement_method_name, | ||
s = second_arg_ident, | ||
r = snippet(cx, right_expr.span, "EXPR") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trailing commas are good
here, and everywhere below (and above)
clippy_lints/src/utils/mod.rs
Outdated
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)` | ||
/// | | ||
/// = note: `-D fold-any` implied by `-D warnings` | ||
/// </pre> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
replace <pre>
with triple back ticks or 4-space indent
clippy_lints/src/methods.rs
Outdated
cx, | ||
UNNECESSARY_FOLD, | ||
fold_span, | ||
// TODO: don't suggest e.g. .any(|x| f(x)) if we can suggest .any(f) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe open an issue for this and link it here?
Also, Clippy has a lint that will suggest using UFCS instead of closures, so a second run will likely trigger (and fix) this anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clippy_lints/src/methods.rs
Outdated
declare_lint! { | ||
pub UNNECESSARY_FOLD, | ||
Warn, | ||
"using `fold` when a more succint alternative exists" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/succint/succinct
clippy_lints/src/methods.rs
Outdated
@@ -623,6 +623,29 @@ declare_lint! { | |||
"using `as_ref` where the types before and after the call are the same" | |||
} | |||
|
|||
|
|||
/// **What it does:** Checks for using `fold` when a more succint alternative exists. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/succint/succinct
51: Fix test edge cases r=oli-obk a=killercup Noticed this while adding test case for ~~fun~~ rust-lang/rust-clippy#2350
51: Fix test edge cases r=oli-obk a=killercup Noticed this while adding test case for ~~fun~~ rust-lang/rust-clippy#2350
Thanks! |
#2237
Some things that need to be addressed before this can be merged. (@Manishearth @oli-obk)
TODO: don't suggest .any(|x| f(x)) if we can suggest .any(f)
(and if so, work out how to do this) (Resolution: won't bother).fold(...)
instead the caller as well (see comments from @killercup about enabling rustfix support)I'll create a new issue for the sum and product cases and handle them in another PR.