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

Needless return #5903

Merged
merged 7 commits into from
Aug 16, 2020
Merged

Needless return #5903

merged 7 commits into from
Aug 16, 2020

Conversation

jrqc
Copy link
Contributor

@jrqc jrqc commented Aug 13, 2020

Fixes #5858
changelog: fix false positive [needless_return]

@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 @matthiaskrgr (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

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 Aug 13, 2020
@matthiaskrgr
Copy link
Member

@bors delegate=ebroto

@bors
Copy link
Collaborator

bors commented Aug 13, 2020

✌️ @ebroto can now approve this pull request

Copy link
Member

@ebroto ebroto left a comment

Choose a reason for hiding this comment

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

This looks good on a general note, thanks!

I left some comments. Also, instead of adding a new module, can we reuse the let_and_return one?. This way we don't have to repeat the code to check if the last statement borrows. I would suggest to change the name of the current returns.rs to unused_unit.rs because it's the last lint remaining there, and rename let_and_return.rs to returns.rs.

}
}

fn check_final_expr(cx: &LateContext<'_>, expr: &Expr<'_>, span: Option<Span>, replacement: RetReplacement) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
fn check_final_expr(cx: &LateContext<'_>, expr: &Expr<'_>, span: Option<Span>, replacement: RetReplacement) {
fn check_final_expr<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
span: Option<Span>,
replacement: RetReplacement,
) {
if last_statement_borrows(cx, expr) {
return;
}

This way we could get rid of the rest of calls to last_statement_borrows.

Copy link
Member

@ebroto ebroto Aug 13, 2020

Choose a reason for hiding this comment

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

Oh sorry, I just realized the placement of the call to last_statement_borrows is wrong. This would lead to false negatives in cases like the following

        fn read_line2(value: bool) -> String {
            if value {
                use std::io::BufRead;
                let stdin = ::std::io::stdin();
                let _a = stdin.lock().lines().next().unwrap().unwrap();
                return String::from("test");
            } else {
                return String::new();
            }
        }

because the visitor would walk recursively and see the borrow in the if block. The check should be done in the ExprKind::Ret(..) part, I'm adding a further comment.

We could add this snippet as an extra test too.

attr.meta_item_list().is_some() && attr.has_name(sym!(cfg))
}

fn check_block_return(cx: &LateContext<'_>, block: &Block<'_>) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
fn check_block_return(cx: &LateContext<'_>, block: &Block<'_>) {
fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) {

We need explicit lifetimes here to apply my previous suggestion.

Comment on lines 58 to 62
FnKind::Closure(_) => {
if !last_statement_borrows(cx, &body.value) {
check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty)
}
},
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
FnKind::Closure(_) => {
if !last_statement_borrows(cx, &body.value) {
check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty)
}
},
FnKind::Closure(_) => check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty),

We can get rid of the if here after moving the call to last_statement_borrows

Comment on lines 65 to 78
if let Some(expr) = block.expr {
if !last_statement_borrows(cx, expr) {
check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty);
}
} else if let Some(stmt) = block.stmts.iter().last() {
match stmt.kind {
StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => {
if !last_statement_borrows(cx, expr) {
check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
}
},
_ => (),
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if let Some(expr) = block.expr {
if !last_statement_borrows(cx, expr) {
check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty);
}
} else if let Some(stmt) = block.stmts.iter().last() {
match stmt.kind {
StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => {
if !last_statement_borrows(cx, expr) {
check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
}
},
_ => (),
}
}
check_block_return(cx, block);

We can simplify here now too

ExprKind::Ret(ref inner) => {
// allow `#[cfg(a)] return a; #[cfg(b)] return b;`
if !expr.attrs.iter().any(attr_is_cfg) {
emit_return_lint(
Copy link
Member

Choose a reason for hiding this comment

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

Before emitting, we could add something like:

                let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
                if !borrows {

@jrqc
Copy link
Contributor Author

jrqc commented Aug 13, 2020

Thanks!

I will publish the changes according to the latest review

Copy link
Member

@ebroto ebroto left a comment

Choose a reason for hiding this comment

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

Almost there, just some small changes!

Also, could you:

  • Change the "changelog:" line to show the lint between backticks like this: [needless_return]? This way the link in the CHANGELOG.md will work.
  • Please rebase to get rid of the merge commits.

Comment on lines 163 to 165
if last_statement_borrows(cx, expr) {
return;
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if last_statement_borrows(cx, expr) {
return;
}

Sorry if I was not clear, this early return should be removed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My bad, I misunderstood the change

return stdin.lock().lines().next().unwrap().unwrap();
}

fn read_line2(value: bool) -> String {
Copy link
Member

Choose a reason for hiding this comment

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

We should check that this is linted because the borrow does not happen in the last statement of the block. Removing the early return I mentioned in my previous comment will fix this.

We could maybe change the name to make the intent more clear, what about borrows_but_not_last?

mod no_lint_if_stmt_borrows {
mod issue_5858 {
fn read_line() -> String {
use std::io::BufRead;
Copy link
Member

Choose a reason for hiding this comment

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

Let's factor out this import at the module level, removing this one and the one in the other function.

Comment on lines 19 to 20
/// **Known problems:** The lint currently misses unit return types in types,
/// e.g., the `F` in `fn generic_unit<F: Fn() -> ()>(f: F) { .. }`.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/// **Known problems:** The lint currently misses unit return types in types,
/// e.g., the `F` in `fn generic_unit<F: Fn() -> ()>(f: F) { .. }`.
/// **Known problems:** None.

Now that we're at it, could you make this change, please? I should have done it in a previous PR of mine 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure :)

@jrqc
Copy link
Contributor Author

jrqc commented Aug 14, 2020

Thanks for the review again!
I've just completed the necessary changes

Copy link
Member

@ebroto ebroto left a comment

Choose a reason for hiding this comment

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

Thanks! There seems to be a problem with the rebase though, the merge commits are still there and there's now additional commits from other PRs.

The process for doing the rebase should be something like the following. There are other ways, this is how I would do it (well, how my editor does it 😄):

# If you already have the remote added as "upstream", skip this first step
git remote add -f upstream https\://github.com/rust-lang/rust-clippy.git

# This will update our local master
git fetch --all
git checkout master
git reset --hard upstream/master

# This will be the actual rebase
git checkout needless_return
git rebase --interactive master

# When the editor window appears, keep just your commits. Since we have master pointing 
# to the latests upstream/master, maybe git will see that and only show your commits,  
# in that case you're already good to go

git push --force

After that only your commits should be on your branch.

If you have any problem with the process, we can make it work for you, but we will have to ping an actual collaborator since I don't have permissions to write to your fork (don't worry, I have to ping them anyway for a last review 😄).

@jrqc
Copy link
Contributor Author

jrqc commented Aug 15, 2020

hi @ebroto
Your suggestion worked pretty well thanks!

@ebroto
Copy link
Member

ebroto commented Aug 15, 2020

LGTM, thanks!

The CI errors are unrelated to your PR and have been fixed by this other PR, another rebase should do the job. FYI this happens once in a while, when rustc breaks clippy the fixes need to be synced.

cc @flip1995 this should be ready to merge when CI passes.

@flip1995
Copy link
Member

Thanks!

@bors r=ebroto,flip1995

Thanks for the review @ebroto

@bors
Copy link
Collaborator

bors commented Aug 16, 2020

📌 Commit baa4cb1 has been approved by ebroto,flip1995

@bors
Copy link
Collaborator

bors commented Aug 16, 2020

⌛ Testing commit baa4cb1 with merge dff7e74...

@bors
Copy link
Collaborator

bors commented Aug 16, 2020

☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test
Approved by: ebroto,flip1995
Pushing dff7e74 to master...

@bors bors merged commit dff7e74 into rust-lang:master Aug 16, 2020
@jrqc jrqc deleted the needless_return branch August 17, 2020 08:43
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.

needless_return false positive when a temporary borrows a local variable
6 participants