Skip to content

Commit eba44e1

Browse files
committed
question_mark: Suggest Some(opt?) for if-else
1 parent eb54c1a commit eba44e1

File tree

3 files changed

+64
-8
lines changed

3 files changed

+64
-8
lines changed

clippy_lints/src/question_mark.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use if_chain::if_chain;
1717

1818
use crate::rustc_errors::Applicability;
1919
use crate::utils::paths::*;
20-
use crate::utils::{match_def_path, match_type, span_lint_and_then};
20+
use crate::utils::{match_def_path, match_type, span_lint_and_then, SpanlessEq};
2121

2222
/// **What it does:** Checks for expressions that could be replaced by the question mark operator
2323
///
@@ -64,14 +64,40 @@ impl Pass {
6464
/// If it matches, it will suggest to use the question mark operator instead
6565
fn check_is_none_and_early_return_none(cx: &LateContext<'_, '_>, expr: &Expr) {
6666
if_chain! {
67-
if let ExprKind::If(ref if_expr, ref body, _) = expr.node;
68-
if let ExprKind::MethodCall(ref segment, _, ref args) = if_expr.node;
67+
if let ExprKind::If(if_expr, body, else_) = &expr.node;
68+
if let ExprKind::MethodCall(segment, _, args) = &if_expr.node;
6969
if segment.ident.name == "is_none";
7070
if Self::expression_returns_none(cx, body);
7171
if let Some(subject) = args.get(0);
7272
if Self::is_option(cx, subject);
7373

7474
then {
75+
if let Some(else_) = else_ {
76+
if_chain! {
77+
if let ExprKind::Block(block, None) = &else_.node;
78+
if block.stmts.len() == 0;
79+
if let Some(block_expr) = &block.expr;
80+
if SpanlessEq::new(cx).ignore_fn().eq_expr(subject, block_expr);
81+
then {
82+
span_lint_and_then(
83+
cx,
84+
QUESTION_MARK,
85+
expr.span,
86+
"this block may be rewritten with the `?` operator",
87+
|db| {
88+
db.span_suggestion_with_applicability(
89+
expr.span,
90+
"replace_it_with",
91+
format!("Some({}?)", Sugg::hir(cx, subject, "..")),
92+
Applicability::MaybeIncorrect, // snippet
93+
);
94+
}
95+
)
96+
}
97+
}
98+
return;
99+
}
100+
75101
span_lint_and_then(
76102
cx,
77103
QUESTION_MARK,

tests/ui/question_mark.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,22 @@ pub struct SomeStruct {
4242
}
4343

4444
impl SomeStruct {
45+
#[rustfmt::skip]
4546
pub fn func(&self) -> Option<u32> {
4647
if (self.opt).is_none() {
4748
return None;
4849
}
4950

51+
if self.opt.is_none() {
52+
return None
53+
}
54+
55+
let _ = if self.opt.is_none() {
56+
return None;
57+
} else {
58+
self.opt
59+
};
60+
5061
self.opt
5162
}
5263
}

tests/ui/question_mark.stderr

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,31 @@ error: this block may be rewritten with the `?` operator
99
= note: `-D clippy::question-mark` implied by `-D warnings`
1010

1111
error: this block may be rewritten with the `?` operator
12-
--> $DIR/question_mark.rs:46:9
12+
--> $DIR/question_mark.rs:47:9
1313
|
14-
46 | / if (self.opt).is_none() {
15-
47 | | return None;
16-
48 | | }
14+
47 | / if (self.opt).is_none() {
15+
48 | | return None;
16+
49 | | }
1717
| |_________^ help: replace_it_with: `(self.opt)?;`
1818

19-
error: aborting due to 2 previous errors
19+
error: this block may be rewritten with the `?` operator
20+
--> $DIR/question_mark.rs:51:9
21+
|
22+
51 | / if self.opt.is_none() {
23+
52 | | return None
24+
53 | | }
25+
| |_________^ help: replace_it_with: `self.opt?;`
26+
27+
error: this block may be rewritten with the `?` operator
28+
--> $DIR/question_mark.rs:55:17
29+
|
30+
55 | let _ = if self.opt.is_none() {
31+
| _________________^
32+
56 | | return None;
33+
57 | | } else {
34+
58 | | self.opt
35+
59 | | };
36+
| |_________^ help: replace_it_with: `Some(self.opt?)`
37+
38+
error: aborting due to 4 previous errors
2039

0 commit comments

Comments
 (0)