-
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
new lint: Unintentional return of unit from closures expecting Ord #5348
Conversation
5a3624b
to
418179d
Compare
☔ The latest upstream changes (presumably #5319) made this pull request unmergeable. Please resolve the merge conflicts. |
This lint catches cases where the last statement of a closure expecting an instance of Ord has a trailing semi-colon. It compiles since the closure ends up return () which also implements Ord but causes unexpected results in cases such as sort_by_key. Fixes rust-lang#5080
☔ The latest upstream changes (presumably #5438) made this pull request unmergeable. Please resolve the merge conflicts. |
gentle reminder for review |
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 add some documentation what the get_*
functions are trying to get. You could just copy the debug output of these types.
if_chain! { | ||
if let ExprKind::MethodCall(_, _, ref args) = expr.kind; |
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.
No need for if_chain if there is only one if
// Trying to call erase_late_bound_regions on fn_sig.inputs() gives the following error | ||
// The trait `rustc::ty::TypeFoldable<'_>` is not implemented for `&[&rustc::ty::TyS<'_>]` | ||
let inputs_output = cx.tcx.erase_late_bound_regions(&fn_sig.inputs_and_output()); | ||
inputs_output.iter().enumerate().for_each(|(i, inp)| { |
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 can be done with
inputs_output.iter().enumerate().for_each(|(i, inp)| { | |
inputs_output.iter().rev().skip(1).rev().enumerate().for_each(|(i, inp)| { |
if i == inputs_output.len() - 1 { | ||
return; | ||
} | ||
fn_mut_preds.iter().for_each(|pred| { |
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'd prefer a normal for loop here, since this is not in an iterator chain and for_each
is less readable IMO.
fn_mut_preds.iter().for_each(|pred| { | |
for pred in &fn_mut_preds { |
@@ -71,6 +72,7 @@ pub const ORD: [&str; 3] = ["core", "cmp", "Ord"]; | |||
pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"]; | |||
pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"]; | |||
pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"]; | |||
pub const PARTIAL_ORD: [&str; 3] = ["std", "cmp", "PartialOrd"]; |
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 don't need this, since PartialOrd is a LangItem, which you can get with cx.tcx.lang_items().partial_ord_trait()
if let ExprKind::Closure(_, _fn_decl, body_id, _span, _) = arg.kind { | ||
if_chain! { | ||
let body = cx.tcx.hir().body(body_id); | ||
if let ExprKind::Block(block, _) = body.value.kind; | ||
if let Some(stmt) = block.stmts.last(); | ||
if let StmtKind::Semi(_) = stmt.kind; | ||
then { return Some(stmt) } | ||
} | ||
} | ||
return 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.
Can be simplified to
if let ExprKind::Closure(_, _fn_decl, body_id, _span, _) = arg.kind { | |
if_chain! { | |
let body = cx.tcx.hir().body(body_id); | |
if let ExprKind::Block(block, _) = body.value.kind; | |
if let Some(stmt) = block.stmts.last(); | |
if let StmtKind::Semi(_) = stmt.kind; | |
then { return Some(stmt) } | |
} | |
} | |
return None; | |
if_chain! { | |
if let ExprKind::Closure(_, _fn_decl, body_id, _span, _) = arg.kind; | |
let body = cx.tcx.hir().body(body_id); | |
if let ExprKind::Block(block, _) = body.value.kind; | |
if let Some(stmt) = block.stmts.last(); | |
if let StmtKind::Semi(_) = stmt.kind; | |
then { | |
Some(stmt) | |
} else { | |
None | |
} | |
} |
if_chain! { | ||
let body = cx.tcx.hir().body(body_id); | ||
if let ExprKind::Block(block, _) = body.value.kind; | ||
if let Some(stmt) = block.stmts.last(); |
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 also have to check for block.expr.is_none()
trait_path: &[&str], | ||
) -> Vec<&'tcx Predicate<'tcx>> { | ||
let mut preds = Vec::new(); | ||
generics.predicates.iter().for_each(|(pred, _)| { |
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.
Also here, I'd prefer a for loop
generics.predicates.iter().for_each(|(pred, _)| { | |
for (pred, _) in &generics.predicates { |
preds.push(pred); | ||
} |
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.
Formatting
preds.push(pred); | |
} | |
preds.push(pred); | |
} |
@@ -37,6 +37,7 @@ pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; | |||
pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; | |||
pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"]; | |||
pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; | |||
pub const FN_MUT: [&str; 3] = ["std", "ops", "FnMut"]; |
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 is also a LangItem
Sorry for taking so long for the review. I don't have an excuse, just wasn't really motivated to review PRs recently. |
Thanks for contributing to Clippy! Sadly this PR was not updated in quite some time. If you waited on input from a reviewer, we're sorry that this fell under the radar. If you want to continue to work on this, just reopen the PR and/or ping a reviewer. |
Since this PR is inactive and was open because of a problem I had initially I'm going to try and finish it :-) |
Reprise: new lint: Unintentional return of unit from closures expecting Ord This lint catches cases where the last statement of a closure expecting an instance of Ord has a trailing semi-colon. It compiles since the closure ends up return () which also implements Ord but causes unexpected results in cases such as sort_by_key. Fixes #5080 Reprise of #5348 where I addressed all the comments there
Reprise: new lint: Unintentional return of unit from closures expecting Ord This lint catches cases where the last statement of a closure expecting an instance of Ord has a trailing semi-colon. It compiles since the closure ends up return () which also implements Ord but causes unexpected results in cases such as sort_by_key. Fixes #5080 Reprise of #5348 where I addressed all the comments there changelog: add lint [`unit_return_expecting_ord`]
This lint catches cases where the last statement of a closure expecting
an instance of Ord has a trailing semi-colon. It compiles since the
closure ends up return () which also implements Ord but causes
unexpected results in cases such as sort_by_key.
Fixes #5080
changelog: none