Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 690a6a6

Browse files
committed
manual-unwrap-or / remove unwrap_or_else suggestion due to ownership issues
1 parent a8fb69f commit 690a6a6

File tree

5 files changed

+69
-30
lines changed

5 files changed

+69
-30
lines changed

clippy_lints/src/manual_unwrap_or.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ declare_clippy_lint! {
3333
/// ```
3434
pub MANUAL_UNWRAP_OR,
3535
complexity,
36-
"finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`"
36+
"finds patterns that can be encoded more concisely with `Option::unwrap_or`"
3737
}
3838

3939
declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]);
@@ -83,26 +83,19 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc
8383
if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span);
8484
if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span);
8585
if let Some(indent) = utils::indent_of(cx, expr.span);
86+
if constant_simple(cx, cx.typeck_results(), none_arm.body).is_some();
8687
then {
8788
let reindented_none_body =
8889
utils::reindent_multiline(none_body_snippet.into(), true, Some(indent));
89-
let eager_eval = constant_simple(cx, cx.typeck_results(), none_arm.body).is_some();
90-
let method = if eager_eval {
91-
"unwrap_or"
92-
} else {
93-
"unwrap_or_else"
94-
};
9590
utils::span_lint_and_sugg(
9691
cx,
9792
MANUAL_UNWRAP_OR, expr.span,
98-
&format!("this pattern reimplements `Option::{}`", &method),
93+
"this pattern reimplements `Option::unwrap_or`",
9994
"replace with",
10095
format!(
101-
"{}.{}({}{})",
96+
"{}.unwrap_or({})",
10297
scrutinee_snippet,
103-
method,
104-
if eager_eval { "" } else { "|| " },
105-
reindented_none_body
98+
reindented_none_body,
10699
),
107100
Applicability::MachineApplicable,
108101
);

src/lintlist/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1183,7 +1183,7 @@ vec![
11831183
Lint {
11841184
name: "manual_unwrap_or",
11851185
group: "complexity",
1186-
desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`",
1186+
desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`",
11871187
deprecation: None,
11881188
module: "manual_unwrap_or",
11891189
},

tests/ui/manual_unwrap_or.fixed

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ fn unwrap_or() {
1212
Some(1).unwrap_or(1 + 42);
1313

1414
// multiline case
15-
Some(1).unwrap_or_else(|| {
16-
let a = 1 + 42;
17-
let b = a + 42;
18-
b + 42
15+
#[rustfmt::skip]
16+
Some(1).unwrap_or({
17+
42 + 42
18+
+ 42 + 42 + 42
19+
+ 42 + 42 + 42
1920
});
2021

2122
// string case
@@ -40,6 +41,28 @@ fn unwrap_or() {
4041
None => break,
4142
};
4243
}
44+
45+
// cases where the none arm isn't a constant expression
46+
// are not linted due to potential ownership issues
47+
48+
// ownership issue example, don't lint
49+
struct NonCopyable;
50+
let mut option: Option<NonCopyable> = None;
51+
match option {
52+
Some(x) => x,
53+
None => {
54+
option = Some(NonCopyable);
55+
// some more code ...
56+
option.unwrap()
57+
},
58+
};
59+
60+
// ownership issue example, don't lint
61+
let option: Option<&str> = None;
62+
match option {
63+
Some(s) => s,
64+
None => &format!("{} {}!", "hello", "world"),
65+
};
4366
}
4467

4568
fn main() {}

tests/ui/manual_unwrap_or.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ fn unwrap_or() {
2121
};
2222

2323
// multiline case
24+
#[rustfmt::skip]
2425
match Some(1) {
2526
Some(i) => i,
2627
None => {
27-
let a = 1 + 42;
28-
let b = a + 42;
29-
b + 42
30-
},
28+
42 + 42
29+
+ 42 + 42 + 42
30+
+ 42 + 42 + 42
31+
}
3132
};
3233

3334
// string case
@@ -55,6 +56,28 @@ fn unwrap_or() {
5556
None => break,
5657
};
5758
}
59+
60+
// cases where the none arm isn't a constant expression
61+
// are not linted due to potential ownership issues
62+
63+
// ownership issue example, don't lint
64+
struct NonCopyable;
65+
let mut option: Option<NonCopyable> = None;
66+
match option {
67+
Some(x) => x,
68+
None => {
69+
option = Some(NonCopyable);
70+
// some more code ...
71+
option.unwrap()
72+
},
73+
};
74+
75+
// ownership issue example, don't lint
76+
let option: Option<&str> = None;
77+
match option {
78+
Some(s) => s,
79+
None => &format!("{} {}!", "hello", "world"),
80+
};
5881
}
5982

6083
fn main() {}

tests/ui/manual_unwrap_or.stderr

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,29 +27,29 @@ LL | | None => 1 + 42,
2727
LL | | };
2828
| |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)`
2929

30-
error: this pattern reimplements `Option::unwrap_or_else`
31-
--> $DIR/manual_unwrap_or.rs:24:5
30+
error: this pattern reimplements `Option::unwrap_or`
31+
--> $DIR/manual_unwrap_or.rs:25:5
3232
|
3333
LL | / match Some(1) {
3434
LL | | Some(i) => i,
3535
LL | | None => {
36-
LL | | let a = 1 + 42;
36+
LL | | 42 + 42
3737
... |
38-
LL | | },
38+
LL | | }
3939
LL | | };
4040
| |_____^
4141
|
4242
help: replace with
4343
|
44-
LL | Some(1).unwrap_or_else(|| {
45-
LL | let a = 1 + 42;
46-
LL | let b = a + 42;
47-
LL | b + 42
44+
LL | Some(1).unwrap_or({
45+
LL | 42 + 42
46+
LL | + 42 + 42 + 42
47+
LL | + 42 + 42 + 42
4848
LL | });
4949
|
5050

5151
error: this pattern reimplements `Option::unwrap_or`
52-
--> $DIR/manual_unwrap_or.rs:34:5
52+
--> $DIR/manual_unwrap_or.rs:35:5
5353
|
5454
LL | / match Some("Bob") {
5555
LL | | Some(i) => i,

0 commit comments

Comments
 (0)