4

Check for `<&NotClone as Clone>::clone()` calls and suggest to add Clo...

 1 year ago
source link: https://github.com/rust-lang/rust/pull/112995
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Conversation

Contributor

Added recursive checking back up the HIR to see if a Clone suggestion would be helpful.

Addresses #112857

Largely based on: #112977

Collaborator

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

Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (S-waiting-on-review and S-waiting-on-author) stays updated, invoking these commands when appropriate:

  • @rustbot author: the review is finished, PR author should check the comments and take action accordingly
  • @rustbot review: the author is ready for a review, this PR will be queued again in the reviewer's queue

rustbot

added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

labels

Jun 24, 2023

Contributor

Author

@estebank Forgive if this is stealing thunder as I wouldn't have done this so quickly without your work here, please feel free to pull this into your branch or whatever works best for protocol.

This covers more cases as per the tests though there are still potentially further improvements I may look into at some point as I notice this issue is probably in the same area: #76643

I think this is OK, but there is a danger of heavy recursion in big code bases but I think they'll always terminate fairly quickly or get caught by stack limits (which are probably quite high in compiler world having said that). Would welcome your thoughts on this one?

estebank reacted with heart emoji

Member

Note that there is already the noop_method_call lint, which is being made warn by default in #111916. There is also an MCP for running lints even if errors have occured at compiler-team#633. If both of these were to be merged then I don't think this change would be necessary?

strottos and oli-obk reacted with eyes emoji

Contributor

Author

@fee1-dead Seems fair, the only thing would be this would put it inline with the error itself making it clear this fixes the error where the other would put out a seemingly unrelated warning the user would have to link together, could even get lost in lots of other warnings. At least that's my understanding of it.

This would potentially be helpful to beginners and the like, more so than experienced Rust devs certainly.

But yeah, if we decide to not go with this for that reason that's cool. I'm happy to close if so.

Contributor

If both of these were to be merged then I don't think this change would be necessary?

The issue with lints is that they don't trigger if other errors are present, which is why I find it reasonable to have some duplication between targeted lints and suggestions in errors, to reduce the number of back and forth that users have when fixing an issue.

strottos reacted with thumbs up emoji

Member

Setting to waiting on author per my review.

@rustbot author

strottos reacted with thumbs up emoji

rustbot

added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author.

and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.

labels

Jul 4, 2023

Contributor

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

Contributor

Author

Pushed some more changes, there's still some patterns not covered but it's more inclusive as can be seen from the new tests.

Examples of a pattern not covered is the following, this seems harder:

fn clone_thing(nc: &NotClone) -> Box<NotClone> {
    let ret = Box::new(nc.clone());
    ret
}

where conversely this is covered:

fn clone_thing(nc: &NotClone) -> Box<NotClone> {
    let ret: Box<NotClone> = Box::new(nc.clone());
    ret
}

Not opposed to looking at the above, but I suspect this will take longer, would be interested in what people thought for feedback as to whether the above is worth it @fee1-dead @estebank ?

Contributor

Author

I do think it's an improvement from what it was but I think to cover every pattern will be a lot of work and not sure how much value.

Contributor

Author

@rustbot review

rustbot

added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.

and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author.

labels

Jul 18, 2023

Contributor

Examples of a pattern not covered is the following, this seems harder:

fn clone_thing(nc: &NotClone) -> Box<NotClone> {
    let ret = Box::new(nc.clone());
    ret
}

It is fine to not support every case, particularly when there are generics in place. I think we could eventually look for any arguments in an fn expression for .clone() calls, recursively, but I'd be very concerned with false positives. Maybe if we found all .clone() calls in sub expressions of the pointed to expression, changed their type from borrow to owned and re-ran inference on the whole expression to see if that would fix the issue, but that's more complex than what we set out to do here.

I do think it's an improvement from what it was but I think to cover every pattern will be a lot of work and not sure how much value.

I am of the position of "handle 80% of the cases with 20% of the effort". If we can expand on the coverage after the fact, that's fine.

strottos reacted with thumbs up emoji

Contributor

Author

That was the conclusion I came to as well, I did try exactly that funny enough using the (walk|visit)_<foo> methods but I managed to trigger false positives myself pretty easily that were harder to get rid of than just keep exhaustively check like we've ended up with.

Comment on lines

1546 to 1569

hir::PatKind::Tuple(pats, ..) => match init.kind {

ExprKind::Tup(init_tup) => {

for (pat, init) in zip(pats.iter(), init_tup.iter()) {

if pat.hir_id == *hir_id {

return self.note_type_is_not_clone(

diag,

expected_ty,

found_ty,

init,

);

}

}

}

ExprKind::Block(

hir::Block {

expr:

Some(Expr {

kind: ExprKind::Tup(init_block_expr_tup), ..

}),

..

},

_,

) => {

for (pat, init) in zip(pats.iter(), init_block_expr_tup.iter())

there's a lot of rightward drift here. Perhaps you could add a method to ExprKind that either returns the original expression or the trailing expr of the innermost block? It could be named as something like trailing_expr or innermost_expr. if you add documentation to that it should be fine

Contributor

Author

I agree the right drift isn't great. Not sure I see how this will help at the moment unfortunately though, maybe there's a pattern I'm not seeing, however I can't return an original expression in an ExprKind as it's not an Expr. Plus I'd need to somehow interject that into this match statement.

I'll try and have another look next week to see if I can see a way of taming the right drift.

Contributor

Author

Yeah I think this is tricky, I think I see what you're saying but if I do something like this it's no good for the case below as I need the expr:

impl<'hir> ExprKind<'hir> {
    pub fn innermost_exprkind(&'hir self) -> &'hir ExprKind<'hir> {
        match self {
            ExprKind::Block(Block { expr: Some(Expr { kind, .. }), .. }, _) => kind,
            _ => self,
        }
    }
}

On the other hand if I make it return an Expr to be useful then I can't just return self as it's the wrong type.

Contributor

Author

Think I've managed to tame the right drift now anyway without this. Have a look at the latest when you've a second and see if it's any better. Happy to look again if further improvements can be made.

Comment on lines

1589 to 1597

// If we're a closure recurse back into that closure

hir::ExprKind::Closure(hir::Closure { body: body_id, .. }) => {

let hir::Body { value: body_expr, .. } = self.tcx.hir().body(*body_id);

return self.note_type_is_not_clone(diag, expected_ty, found_ty, body_expr);

}

// Similarly recurse into a block

hir::ExprKind::Block(hir::Block { expr: Some(block_expr), .. }, _) => {

return self.note_type_is_not_clone(diag, expected_ty, found_ty, block_expr);

}

with the trailing_expr abstraction suggested above these duplication wouldn't be necessary i think.

strottos reacted with eyes emoji

Member

@fee1-dead fee1-dead

left a comment

Had a few nits. After addressing those this should be good to go

strottos reacted with heart emoji

Comment on lines

1584 to 1588

match expr.kind {

hir::ExprKind::Path(hir::QPath::Resolved(

None,

hir::Path { segments: [_], res: crate::Res::Local(binding), .. },

)) => {

All the other arms of this match block return. Therefore you can extract the binding like so:

let binding = match expr.kind {
    hir::ExprKind::Path(hir::QPath::Resolved(
        None,
        hir::Path { segments: [_], res: crate::Res::Local(binding), .. },
    )) => {
        binding
    }
    /* other arms return */
};

Contributor

Author

Good suggestion but it’s no longer true that all the other branches return now we’re not returning an Option. I think this is clearer as is personally, but happy to change if you still think.

This comment has been minimized.

Contributor

Author

Thanks @fee1-dead , think I’ve addressed all but the last one which I don’t think is relevant any more. Thanks for your time.

Member

@fee1-dead fee1-dead

left a comment

Just had a few final nits. Sorry for this back and forth!

strottos reacted with thumbs up emoji

Comment on lines

1613 to 1622

match init.kind {

ExprKind::Tup(init_tup) => {

if let Some(init) = find_init(init_tup) {

self.note_type_is_not_clone_inner_expr(init)

} else {

expr

}

}

ExprKind::Block(_, _) => {

let block_expr = init.peel_blocks();

Member

I meant you call peel_blocks and just do if let ExprKind::Tup(init_tup) = &init.peel_blocks().kind. That way you don't have to worry about matching the block case at all. With that you don't need to have duplicate arms or a closure either. Sorry for not being clear about this.

self.note_type_is_not_clone_inner_expr(block_expr)

}

hir::ExprKind::Call(Expr { kind: call_expr_kind, .. }, _) => {

if let hir::ExprKind::Path(hir::QPath::Resolved( None, call_expr_path,)) = call_expr_kind

Suggested change
if let hir::ExprKind::Path(hir::QPath::Resolved( None, call_expr_path,)) = call_expr_kind
if let hir::ExprKind::Path(hir::QPath::Resolved(None, call_expr_path)) = call_expr_kind

Comment on lines

1650 to 1653

&& let Some(closure) = self.tcx.hir().find(self.tcx.hir().parent_id(*hir_id))

&& let hir::Node::Local(hir::Local { init: Some(init), .. }) = closure

{

self.note_type_is_not_clone_inner_expr(init)

this seems to be specific to the clone_thing9 test case? If this is so and this is either a closure or something else that we don't care about, I'd prefer this to extract the closure body here instead of in a match case above. Therefore you should remove that match arm and move that here.

Also, could you add some comments on this if let chain, to explain what we are trying to do here (lookup the source of a closure call)?

Comment on lines

1642 to 1645

// Similarly recurse into a block

hir::ExprKind::Block(hir::Block { expr: Some(block_expr), .. }, _) => {

self.note_type_is_not_clone_inner_expr(block_expr)

}

similar to the peel_blocks use above it would be nice if expr.peel_blocks() is used for this match also. Then we'd avoid the unnecessary recursion and reduce the noise from handling not very relevant cases.

Member

@fee1-dead fee1-dead

left a comment

LGTM, thanks!

strottos reacted with heart emoji

Member

@bors r+

Contributor

📌 Commit 25db1fa has been approved by fee1-dead

It is now in the queue for this repository.

bors

added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.

and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.

labels

Jul 25, 2023

Contributor

Author

LGTM, thanks!

Thanks for your time again and no need to apologise, learned loads from doing this and been a great experience grinning

fee1-dead reacted with heart emoji

Contributor

hourglass Testing commit 25db1fa with merge 0c0090f...

Collaborator

The job i686-mingw failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)

Contributor

broken_heart Test failed - checks-actions

fee1-dead and strottos reacted with confused emoji

bors

added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.

and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.

labels

Jul 25, 2023

Contributor

Author

Had a quick look and looks kind of like a spurious failure. It failed because the backtrack on a Windows PC stopped spitting out function names and just wrote out {unknown} in place. Currently unsure why this would happen but my guess is a retry would work. Certainly this isn’t an area I’ve directly touched:

2023-07-25T11:57:15.0014906Z stack backtrace:
2023-07-25T11:57:15.0015160Z    0: 0x662c2d42 - core::fmt::write::h1147d65d38424eec
2023-07-25T11:57:15.0015437Z    1: 0x66256614 - std::io::Write::write_fmt::hb78e5874c67659c6
2023-07-25T11:57:15.0015721Z    2: 0x662603c0 - std::sys_common::backtrace::print::hd10d5c20761406e1
2023-07-25T11:57:15.0016060Z    3: 0x662636cd - std::panicking::panic_hook_with_disk_dump::{{closure}}::h00d490f5d4e50606
2023-07-25T11:57:15.0016511Z    4: 0x662634b3 - std::panicking::panic_hook_with_disk_dump::hebe5fc45befd04f3
2023-07-25T11:57:15.0016828Z    5: 0x66d1f227 - rustc_driver_impl[a5419d7a2f24870a]::install_ice_hook::{closure#0}
2023-07-25T11:57:15.0017144Z    6: 0x66263f19 - std::panicking::rust_panic_with_hook::h4ff6de34a70869f0
2023-07-25T11:57:15.0017457Z    7: 0x66263cc8 - std::panicking::begin_panic_handler::{{closure}}::haf05a0f3d7198621
2023-07-25T11:57:15.0017837Z    8: 0x66261158 - std::sys_common::backtrace::__rust_end_short_backtrace::ha215c523c59793aa
2023-07-25T11:57:15.0018107Z    9: 0x66263a55 - _rust_begin_unwind
2023-07-25T11:57:15.0018363Z   10: 0x662bea6d - core::panicking::panic_fmt::hff163fd7d423cd54
2023-07-25T11:57:15.0018675Z   11: 0x69d40fc7 - <rustc_errors[37c6311b894514b8]::HandlerInner>::panic_if_treat_err_as_bug
2023-07-25T11:57:15.0019003Z   12: 0x69d40126 - <rustc_errors[37c6311b894514b8]::HandlerInner>::emit_diagnostic::{closure#2}
2023-07-25T11:57:15.0019318Z   13: 0x66f6bf79 - rustc_interface[90485b44df649e4f]::callbacks::track_diagnostic
2023-07-25T11:57:15.0019626Z   14: 0x69d3f48f - <rustc_errors[37c6311b894514b8]::HandlerInner>::emit_diagnostic
2023-07-25T11:57:15.0020027Z   15: 0x69d8a538 - <rustc_span[816e203749c7a62]::ErrorGuaranteed as rustc_errors[37c6311b894514b8]::diagnostic_builder::EmissionGuarantee>::diagnostic_builder_emit_producing_guarantee
2023-07-25T11:57:15.0020428Z   16: 0x67edbf3f - <rustc_resolve[9998538d989d25aa]::Resolver>::report_errors
2023-07-25T11:57:15.0020785Z   17: 0x67fe959a - <rustc_session[7093658e48485c60]::session::Session>::time::<(), <rustc_resolve[9998538d989d25aa]::Resolver>::resolve_crate::{closure#0}>
2023-07-25T11:57:15.0021070Z   18:    0x3d4d0 - <unknown>
2023-07-25T11:57:15.0021258Z   19:    0x3d4d0 - <unknown>
2023-07-25T11:57:15.0021371Z 
2023-07-25T11:57:15.0021541Z error: the compiler unexpectedly panicked. this is a bug

When tested on a Linux PC (all I have access to right now, tomorrow can try on a Windows PC tomorrow evening) we get much more output than this that would match the test. Is there a way we can queue a retry @fee1-dead ? No stress on this one, it’s not a biggie.

Would be curious to know under what circumstances we get this… could be some obscure Windows internals maybe but would need to look how backtrack gets this detail but it’s bound to be a Windows syscall equivalent (forget the name).

Contributor

Author

Ran that test 1k times on my Linux box and 1k correct outputs. Wonder if the same will be true on the Windows box tomorrow evening. Either way whatever happens to this I’ll look into that again there tomorrow so even if it is something I’ve done hopefully I’ll be able to see what (though I can’t think what that could have been at present).

Member

Let's just retry and see what happens.

@bors retry

strottos reacted with thumbs up emoji

bors

added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.

and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.

labels

Jul 25, 2023

bors

merged commit c5c0aa1 into

rust-lang:master

Jul 26, 2023

11 of 12 checks passed

rust-timer

added a commit to rust-lang-ci/rust that referenced this pull request

Jul 26, 2023

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

fee1-dead

fee1-dead approved these changes
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects

None yet

Milestone

1.73.0

Development

Successfully merging this pull request may close these issues.

None yet

6 participants

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK