-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Add unnecessary_to_owned
lint
#7978
Conversation
r? @flip1995 (rust-highfive has picked a reviewer for you, use r? to override) |
Oh this is a nice lint! I only skimmed the code. It'll probably take me a few days to do a in-depth review. In the mean time, could you add some documentation to the functions that makes it easier to understand what those functions are checking for, please? Especially the
Shouldn't |
Thank you. :) I will.
Sorry for not explaining well. Currently, I call fn foo(_: impl AsRef<str>) {}
fn bar<T: AsRef<str>>(_: T) {}
([impl AsRef<str>]; c_variadic: false)->()
([T]; c_variadic: false)->() Currently, the lint catches calls to the Presumably, there is some rustc API that allows one to query this, e.g., "What are the bounds on the type parameters of the function with definition id |
☔ The latest upstream changes (presumably #7896) made this pull request unmergeable. Please resolve the merge conflicts. |
The |
Well, it could be that I am misunderstanding something.
I think once you have I'm not sure That's sort of the crux of the issue: how do you find out what those bounds were? |
The So it doesn't really matter what bounds the argument type had in the code. After trait resolution and type checking, the generic type should implement the trait, no matter what actual type it is. |
Sorry, I think I may have just found the answer: https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.predicates_of Please give me a few days to look into this and update the lint. I apologize for my confusion. |
1f2c732
to
24ac43c
Compare
@flip1995 I think this is working as it should and you are welcome to start reviewing whenever you have time. |
I just want to make sure we are careful with how this lint overlaps with |
How would you handle this, @camsteffen? Is there a way to, say, to call |
Maybe you could do it the other way around - call the new lint implemention inside of redundant_clone. |
Actually maybe a better strategy is the following: have this lint apply only when the receiver is copy-able, or when the I think that would eliminate the possibility of overlap between An alternative might be to do just the first part and extend |
I was just thinking of cases like |
OK. Then I will implement what I called a "better strategy" above. That should handle the case that you mentioned, and is less invasive than the other options (no changes to I will also implement the list of methods shared with @flip1995 Please give me a day or two to implement these changes. |
@flip1995 I think the just pushed changes address all of @camsteffen's concerns. |
* Share a list of methods with `implicit_clone` * Ensure no overlap with `redundant_clone`
clippy_lints/src/methods/mod.rs
Outdated
NEEDLESS_SPLITN | ||
NEEDLESS_SPLITN, | ||
UNNECESSARY_TO_OWNED |
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.
IMHO this cloud/should have a trailing ,
so that the diff is only one insertion, instead of 1 delete, 2 insertions
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 agree.
@@ -169,7 +169,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { | |||
trait_name | |||
), | |||
Some(last_semi), | |||
&"probably caused by this trailing semicolon".to_string(), | |||
"probably caused by this trailing semicolon", |
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.
🥳
@smoelius can you please test the following code if the new lint triggers incorrectly? #![feature(drain_filter)]
fn main() {
let mut a = vec![3, 5, 7, -4, -6];
for x in a.drain_filter(|x| *x < 0).collect::<Vec<_>>() {
a.push(-x);
}
} The problem is, that I can't remove |
For me, the new lint doesn't seem to fire on that example. Please tell me if you observe something else. |
that's great <3
I haven't observed anything, because I didn't want to compile clippy on my machine ;) That's why I asked you to run that code for me and as it turns out, it looks fine :) |
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 impressed by the very comprehensive test suite. 👍 I only found a few small nits. With those picked, I think this should be deemed mergeable.
clippy_lints/src/methods/mod.rs
Outdated
/// and other `to_owned`-like functions. | ||
/// | ||
/// ### Why is this bad? | ||
/// The unnecessary calls result in unnecessary allocations. |
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.
Style: "unnecessary" used twice. How about changing one to "needless" or "useless"?
/// ### Example | ||
/// ```rust | ||
/// let path = std::path::Path::new("x"); | ||
/// foo(&path.to_string_lossy().to_string()); |
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.
Is to_string
even caught here? I didn't find any mention of it in is_clone_like
and wondered if that was covered by another lint.
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.
to_string
is handled outside of is_clone_like
:
|| is_to_string(cx, method_name, method_def_id) |
rust-clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
Lines 399 to 401 in cb609a9
fn is_to_string(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { | |
method_name.as_str() == "to_string" && is_diag_trait_item(cx, method_def_id, sym::ToString) | |
} |
The reason for this is that
implicit_clone
calls is_clone_like
(as a consequence of this PR), and I didn't want to change implicit_clone
's behavior:if is_clone_like(cx, method_name, method_def_id); |
Please let me know if you think something should be adjusted.
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.
Then we should at least have a comment stating 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.
Please tell me if what I wrote is inadequate.
then { | ||
let (target_ty, n_target_refs) = peel_mid_ty_refs(target_ty); | ||
let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty); | ||
if_chain! { |
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 if_chain!
has only two conditions that could simply be put together with &&
, thus reducing rightwards drift.
@llogiq Thank for the review and for your kind words. Please let me know if you have any comments following the adjustments. |
This looks good now. Thank you! @bors r+ |
📌 Commit b891389 has been approved by |
☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test |
This PR adds a lint to check for unnecessary calls to
ToOwned::to_owned
and other similar functions (e.g.,Cow::into_owned
,ToString::to_string
, etc.).The lint checks for expressions of the form
&receiver.to_owned_like()
used in a position requiring type&T
where one of the following is true:receiver
's type isT
exactlyreceiver
's type implementsDeref<Target = T>
receiver
's type implementsAsRef<T>
The lint additionally checks for expressions of the form
receiver.to_owned_like()
used as arguments of typeimpl AsRef<T>
.It would be nice if the lint could also check for expressions used as arguments to functions like the following:
However, I couldn't figure out how to determine whether a function input type was instantiated from a parameter with a trait bound.
If someone could offer me some guidance, I would be happy to add such functionality.
Closes #7933
changelog: Add [
unnecessary_to_owned
] lint