Skip to content

Commit d5e7bed

Browse files
committed
Properly parenthesize to avoid operator precedence errors
1 parent eb19d41 commit d5e7bed

File tree

4 files changed

+53
-10
lines changed

4 files changed

+53
-10
lines changed

clippy_lints/src/option_if_let_else.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,12 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -
175175
let capture_name = id.name.to_ident_string();
176176
let wrap_braces = utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| {
177177
if_chain! {
178-
if let Some(Expr { kind: ExprKind::Match(condition, arms, MatchSource::IfDesugar{contains_else_clause: true}|MatchSource::IfLetDesugar{contains_else_clause: true}), .. } ) = parent.expr;
178+
if let Some(Expr { kind: ExprKind::Match(
179+
_,
180+
arms,
181+
MatchSource::IfDesugar{contains_else_clause: true}
182+
| MatchSource::IfLetDesugar{contains_else_clause: true}),
183+
.. } ) = parent.expr;
179184
if expr.hir_id == arms[1].body.hir_id;
180185
then {
181186
true
@@ -184,8 +189,19 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -
184189
}
185190
}
186191
});
192+
let parens_around_option = match &let_body.kind {
193+
ExprKind::Call(..)
194+
| ExprKind::MethodCall(..)
195+
| ExprKind::Loop(..)
196+
| ExprKind::Match(..)
197+
| ExprKind::Block(..)
198+
| ExprKind::Field(..)
199+
| ExprKind::Path(_)
200+
=> false,
201+
_ => true,
202+
};
187203
Some(OptionIfLetElseOccurence {
188-
option: format!("{}", Sugg::hir(cx, let_body, "..")),
204+
option: format!("{}{}{}", if parens_around_option { "(" } else { "" }, Sugg::hir(cx, let_body, ".."), if parens_around_option { ")" } else { "" }),
189205
method_sugg: format!("{}", method_sugg),
190206
some_expr: format!("|{}| {}", capture_name, Sugg::hir(cx, some_body, "..")),
191207
none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")),
@@ -209,7 +225,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OptionIfLetElse {
209225
format!(
210226
"{}{}.{}({}, {}){}",
211227
if detection.wrap_braces { "{ " } else { "" },
212-
detection.option, detection.method_sugg, detection.none_expr, detection.some_expr,
228+
detection.option,
229+
detection.method_sugg,
230+
detection.none_expr,
231+
detection.some_expr,
213232
if detection.wrap_braces { " }" } else { "" },
214233
),
215234
Applicability::MachineApplicable,

tests/ui/option_if_let_else.fixed

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@ fn bad1(string: Option<&str>) -> (bool, &str) {
55
string.map_or((false, "hello"), |x| (true, x))
66
}
77

8-
fn bad2(string: Option<&str>) -> Option<(bool, &str)> {
8+
fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
99
if string.is_none() {
1010
None
1111
} else { string.map_or(Some((false, "")), |x| Some((true, x))) }
1212
}
1313

14+
fn unop_bad(string: &Option<&str>) -> usize {
15+
(*string).map_or(0, |s| s.len())
16+
}
17+
1418
fn longer_body(arg: Option<u32>) -> u32 {
1519
arg.map_or(13, |x| {
1620
let y = x * x;
@@ -48,7 +52,8 @@ fn main() {
4852
let optional = Some(5);
4953
let _ = optional.map_or(5, |x| x + 2);
5054
let _ = bad1(None);
51-
let _ = bad2(None);
55+
let _ = else_if_option(None);
56+
let _ = unop_bad(&None);
5257
let _ = longer_body(None);
5358
test_map_or_else(None);
5459
let _ = negative_tests(None);

tests/ui/option_if_let_else.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ fn bad1(string: Option<&str>) -> (bool, &str) {
99
}
1010
}
1111

12-
fn bad2(string: Option<&str>) -> Option<(bool, &str)> {
12+
fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
1313
if string.is_none() {
1414
None
1515
} else if let Some(x) = string {
@@ -19,6 +19,14 @@ fn bad2(string: Option<&str>) -> Option<(bool, &str)> {
1919
}
2020
}
2121

22+
fn unop_bad(string: &Option<&str>) -> usize {
23+
if let Some(s) = *string {
24+
s.len()
25+
} else {
26+
0
27+
}
28+
}
29+
2230
fn longer_body(arg: Option<u32>) -> u32 {
2331
if let Some(x) = arg {
2432
let y = x * x;
@@ -60,7 +68,8 @@ fn main() {
6068
let optional = Some(5);
6169
let _ = if let Some(x) = optional { x + 2 } else { 5 };
6270
let _ = bad1(None);
63-
let _ = bad2(None);
71+
let _ = else_if_option(None);
72+
let _ = unop_bad(&None);
6473
let _ = longer_body(None);
6574
test_map_or_else(None);
6675
let _ = negative_tests(None);

tests/ui/option_if_let_else.stderr

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ LL | | }
2424
error: use Option::map_or instead of an if let/else
2525
--> $DIR/option_if_let_else.rs:23:5
2626
|
27+
LL | / if let Some(s) = *string {
28+
LL | | s.len()
29+
LL | | } else {
30+
LL | | 0
31+
LL | | }
32+
| |_____^ help: try: `(*string).map_or(0, |s| s.len())`
33+
34+
error: use Option::map_or instead of an if let/else
35+
--> $DIR/option_if_let_else.rs:31:5
36+
|
2737
LL | / if let Some(x) = arg {
2838
LL | | let y = x * x;
2939
LL | | y * y
@@ -41,7 +51,7 @@ LL | })
4151
|
4252

4353
error: use Option::map_or_else instead of an if let/else
44-
--> $DIR/option_if_let_else.rs:32:13
54+
--> $DIR/option_if_let_else.rs:40:13
4555
|
4656
LL | let _ = if let Some(x) = arg {
4757
| _____________^
@@ -64,10 +74,10 @@ LL | }, |x| x * x * x * x);
6474
|
6575

6676
error: use Option::map_or instead of an if let/else
67-
--> $DIR/option_if_let_else.rs:61:13
77+
--> $DIR/option_if_let_else.rs:69:13
6878
|
6979
LL | let _ = if let Some(x) = optional { x + 2 } else { 5 };
7080
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
7181

72-
error: aborting due to 5 previous errors
82+
error: aborting due to 6 previous errors
7383

0 commit comments

Comments
 (0)