Skip to content

or_fun_call: lint method calls inside map_or first arg #15074

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions clippy_lints/src/methods/or_fun_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ pub(super) fn check<'tcx>(
fun_span: Option<Span>,
) -> bool {
// (path, fn_has_argument, methods, suffix)
const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 5] = [
const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 7] = [
(sym::BTreeEntry, false, &[sym::or_insert], "with"),
(sym::HashMapEntry, false, &[sym::or_insert], "with"),
(
Expand All @@ -146,7 +146,9 @@ pub(super) fn check<'tcx>(
"else",
),
(sym::Option, false, &[sym::get_or_insert], "with"),
(sym::Option, true, &[sym::and], "then"),
(sym::Result, true, &[sym::map_or, sym::or, sym::unwrap_or], "else"),
(sym::Result, true, &[sym::and], "then"),
];

if KNOW_TYPES.iter().any(|k| k.2.contains(&name))
Expand Down Expand Up @@ -240,15 +242,23 @@ pub(super) fn check<'tcx>(
let inner_arg = peel_blocks(arg);
for_each_expr(cx, inner_arg, |ex| {
let is_top_most_expr = ex.hir_id == inner_arg.hir_id;
if let hir::ExprKind::Call(fun, fun_args) = ex.kind {
let fun_span = if fun_args.is_empty() && is_top_most_expr {
Some(fun.span)
} else {
None
};
if check_or_fn_call(cx, name, method_span, receiver, arg, Some(lambda), expr.span, fun_span) {
return ControlFlow::Break(());
}
match ex.kind {
hir::ExprKind::Call(fun, fun_args) => {
let fun_span = if fun_args.is_empty() && is_top_most_expr {
Some(fun.span)
} else {
None
};
if check_or_fn_call(cx, name, method_span, receiver, arg, Some(lambda), expr.span, fun_span) {
return ControlFlow::Break(());
}
},
hir::ExprKind::MethodCall(..) => {
if check_or_fn_call(cx, name, method_span, receiver, arg, Some(lambda), expr.span, None) {
return ControlFlow::Break(());
}
},
_ => {},
}
ControlFlow::Continue(())
});
Expand Down
12 changes: 8 additions & 4 deletions clippy_lints/src/needless_pass_by_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
for (span, suggestion) in clone_spans {
diag.span_suggestion(
span,
span.get_source_text(cx)
.map_or("change the call to".to_owned(), |src| format!("change `{src}` to")),
span.get_source_text(cx).map_or_else(
|| "change the call to".to_owned(),
|src| format!("change `{src}` to"),
),
suggestion,
Applicability::Unspecified,
);
Expand Down Expand Up @@ -270,8 +272,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
for (span, suggestion) in clone_spans {
diag.span_suggestion(
span,
span.get_source_text(cx)
.map_or("change the call to".to_owned(), |src| format!("change `{src}` to")),
span.get_source_text(cx).map_or_else(
|| "change the call to".to_owned(),
|src| format!("change `{src}` to"),
),
suggestion,
Applicability::Unspecified,
);
Expand Down
11 changes: 7 additions & 4 deletions clippy_utils/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@ fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) {
{
diag.help(format!(
"for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}",
&option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
// extract just major + minor version and ignore patch versions
format!("rust-{}", n.rsplit_once('.').unwrap().1)
})
&option_env!("RUST_RELEASE_NUM").map_or_else(
|| "master".to_string(),
|n| {
// extract just major + minor version and ignore patch versions
format!("rust-{}", n.rsplit_once('.').unwrap().1)
}
)
));
}
}
Expand Down
24 changes: 24 additions & 0 deletions tests/ui/or_fun_call.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@ mod issue8993 {
let _ = Some(4).map_or_else(g, f);
//~^ or_fun_call
let _ = Some(4).map_or(0, f);
let _ = Some(4).map_or_else(|| "asd".to_string().len() as i32, f);
//~^ or_fun_call
}
}

Expand Down Expand Up @@ -426,6 +428,8 @@ mod result_map_or {
let _ = x.map_or_else(|_| g(), f);
//~^ or_fun_call
let _ = x.map_or(0, f);
let _ = x.map_or_else(|_| "asd".to_string().len() as i32, f);
//~^ or_fun_call
}
}

Expand All @@ -439,4 +443,24 @@ fn test_option_get_or_insert() {
//~^ or_fun_call
}

fn test_option_and() {
// assume that this is slow call
fn g() -> Option<u8> {
Some(99)
}
let mut x = Some(42_u8);
let _ = x.and_then(|_| g());
//~^ or_fun_call
}

fn test_result_and() {
// assume that this is slow call
fn g() -> Result<u8, ()> {
Ok(99)
}
let mut x: Result<u8, ()> = Ok(42);
let _ = x.and_then(|_| g());
//~^ or_fun_call
}

fn main() {}
24 changes: 24 additions & 0 deletions tests/ui/or_fun_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@ mod issue8993 {
let _ = Some(4).map_or(g(), f);
//~^ or_fun_call
let _ = Some(4).map_or(0, f);
let _ = Some(4).map_or("asd".to_string().len() as i32, f);
//~^ or_fun_call
}
}

Expand Down Expand Up @@ -426,6 +428,8 @@ mod result_map_or {
let _ = x.map_or(g(), f);
//~^ or_fun_call
let _ = x.map_or(0, f);
let _ = x.map_or("asd".to_string().len() as i32, f);
//~^ or_fun_call
}
}

Expand All @@ -439,4 +443,24 @@ fn test_option_get_or_insert() {
//~^ or_fun_call
}

fn test_option_and() {
// assume that this is slow call
fn g() -> Option<u8> {
Some(99)
}
let mut x = Some(42_u8);
let _ = x.and(g());
//~^ or_fun_call
}

fn test_result_and() {
// assume that this is slow call
fn g() -> Result<u8, ()> {
Ok(99)
}
let mut x: Result<u8, ()> = Ok(42);
let _ = x.and(g());
//~^ or_fun_call
}

fn main() {}
58 changes: 41 additions & 17 deletions tests/ui/or_fun_call.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -154,62 +154,68 @@ error: function call inside of `map_or`
LL | let _ = Some(4).map_or(g(), f);
| ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)`

error: function call inside of `map_or`
--> tests/ui/or_fun_call.rs:286:25
|
LL | let _ = Some(4).map_or("asd".to_string().len() as i32, f);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| "asd".to_string().len() as i32, f)`

error: use of `unwrap_or_else` to construct default value
--> tests/ui/or_fun_call.rs:315:18
--> tests/ui/or_fun_call.rs:317:18
|
LL | with_new.unwrap_or_else(Vec::new);
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`

error: use of `unwrap_or_else` to construct default value
--> tests/ui/or_fun_call.rs:319:28
--> tests/ui/or_fun_call.rs:321:28
|
LL | with_default_trait.unwrap_or_else(Default::default);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`

error: use of `unwrap_or_else` to construct default value
--> tests/ui/or_fun_call.rs:323:27
--> tests/ui/or_fun_call.rs:325:27
|
LL | with_default_type.unwrap_or_else(u64::default);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`

error: use of `unwrap_or_else` to construct default value
--> tests/ui/or_fun_call.rs:327:22
--> tests/ui/or_fun_call.rs:329:22
|
LL | real_default.unwrap_or_else(<FakeDefault as Default>::default);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`

error: use of `or_insert_with` to construct default value
--> tests/ui/or_fun_call.rs:331:23
--> tests/ui/or_fun_call.rs:333:23
|
LL | map.entry(42).or_insert_with(String::new);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()`

error: use of `or_insert_with` to construct default value
--> tests/ui/or_fun_call.rs:335:25
--> tests/ui/or_fun_call.rs:337:25
|
LL | btree.entry(42).or_insert_with(String::new);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()`

error: use of `unwrap_or_else` to construct default value
--> tests/ui/or_fun_call.rs:339:25
--> tests/ui/or_fun_call.rs:341:25
|
LL | let _ = stringy.unwrap_or_else(String::new);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`

error: function call inside of `unwrap_or`
--> tests/ui/or_fun_call.rs:381:17
--> tests/ui/or_fun_call.rs:383:17
|
LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)`
| ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)`

error: function call inside of `unwrap_or`
--> tests/ui/or_fun_call.rs:386:17
--> tests/ui/or_fun_call.rs:388:17
|
LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)`
| ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)`

error: function call inside of `unwrap_or`
--> tests/ui/or_fun_call.rs:391:17
--> tests/ui/or_fun_call.rs:393:17
|
LL | let _ = opt.unwrap_or({
| _________________^
Expand All @@ -229,40 +235,58 @@ LL ~ });
|

error: function call inside of `map_or`
--> tests/ui/or_fun_call.rs:397:17
--> tests/ui/or_fun_call.rs:399:17
|
LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)`
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)`

error: use of `unwrap_or` to construct default value
--> tests/ui/or_fun_call.rs:402:17
--> tests/ui/or_fun_call.rs:404:17
|
LL | let _ = opt.unwrap_or({ i32::default() });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`

error: function call inside of `unwrap_or`
--> tests/ui/or_fun_call.rs:409:21
--> tests/ui/or_fun_call.rs:411:21
|
LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })`

error: function call inside of `map_or`
--> tests/ui/or_fun_call.rs:424:19
--> tests/ui/or_fun_call.rs:426:19
|
LL | let _ = x.map_or(g(), |v| v);
| ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)`

error: function call inside of `map_or`
--> tests/ui/or_fun_call.rs:426:19
--> tests/ui/or_fun_call.rs:428:19
|
LL | let _ = x.map_or(g(), f);
| ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)`

error: function call inside of `map_or`
--> tests/ui/or_fun_call.rs:431:19
|
LL | let _ = x.map_or("asd".to_string().len() as i32, f);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| "asd".to_string().len() as i32, f)`

error: function call inside of `get_or_insert`
--> tests/ui/or_fun_call.rs:438:15
--> tests/ui/or_fun_call.rs:442:15
|
LL | let _ = x.get_or_insert(g());
| ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)`

error: aborting due to 41 previous errors
error: function call inside of `and`
--> tests/ui/or_fun_call.rs:452:15
|
LL | let _ = x.and(g());
| ^^^^^^^^ help: try: `and_then(|_| g())`

error: function call inside of `and`
--> tests/ui/or_fun_call.rs:462:15
|
LL | let _ = x.and(g());
| ^^^^^^^^ help: try: `and_then(|_| g())`

error: aborting due to 45 previous errors