1
+ use crate :: manual_let_else:: { MatchLintBehaviour , MANUAL_LET_ELSE } ;
1
2
use clippy_utils:: diagnostics:: span_lint_and_sugg;
3
+ use clippy_utils:: msrvs:: Msrv ;
2
4
use clippy_utils:: source:: snippet_with_applicability;
3
5
use clippy_utils:: ty:: is_type_diagnostic_item;
4
6
use clippy_utils:: {
5
- eq_expr_value, get_parent_node, in_constant, is_else_clause, is_res_lang_ctor, path_to_local , path_to_local_id ,
6
- peel_blocks, peel_blocks_with_stmt,
7
+ eq_expr_value, get_parent_node, in_constant, is_else_clause, is_res_lang_ctor, pat_and_expr_can_be_question_mark ,
8
+ path_to_local , path_to_local_id , peel_blocks, peel_blocks_with_stmt,
7
9
} ;
8
10
use clippy_utils:: { higher, is_path_lang_item} ;
9
11
use if_chain:: if_chain;
10
12
use rustc_errors:: Applicability ;
11
13
use rustc_hir:: def:: Res ;
12
14
use rustc_hir:: LangItem :: { self , OptionNone , OptionSome , ResultErr , ResultOk } ;
13
- use rustc_hir:: { BindingAnnotation , ByRef , Expr , ExprKind , Node , PatKind , PathSegment , QPath } ;
15
+ use rustc_hir:: {
16
+ BindingAnnotation , Block , ByRef , Expr , ExprKind , Local , Node , PatKind , PathSegment , QPath , Stmt , StmtKind ,
17
+ } ;
14
18
use rustc_lint:: { LateContext , LateLintPass } ;
15
19
use rustc_middle:: ty:: Ty ;
16
20
use rustc_session:: declare_tool_lint;
@@ -42,16 +46,29 @@ declare_clippy_lint! {
42
46
"checks for expressions that could be replaced by the question mark operator"
43
47
}
44
48
45
- #[ derive( Default ) ]
46
49
pub struct QuestionMark {
50
+ pub ( crate ) msrv : Msrv ,
51
+ pub ( crate ) matches_behaviour : MatchLintBehaviour ,
47
52
/// Keeps track of how many try blocks we are in at any point during linting.
48
53
/// This allows us to answer the question "are we inside of a try block"
49
54
/// very quickly, without having to walk up the parent chain, by simply checking
50
55
/// if it is greater than zero.
51
56
/// As for why we need this in the first place: <https://github.com/rust-lang/rust-clippy/issues/8628>
52
57
try_block_depth_stack : Vec < u32 > ,
53
58
}
54
- impl_lint_pass ! ( QuestionMark => [ QUESTION_MARK ] ) ;
59
+
60
+ impl_lint_pass ! ( QuestionMark => [ QUESTION_MARK , MANUAL_LET_ELSE ] ) ;
61
+
62
+ impl QuestionMark {
63
+ #[ must_use]
64
+ pub fn new ( msrv : Msrv , matches_behaviour : MatchLintBehaviour ) -> Self {
65
+ Self {
66
+ msrv,
67
+ matches_behaviour,
68
+ try_block_depth_stack : Vec :: new ( ) ,
69
+ }
70
+ }
71
+ }
55
72
56
73
enum IfBlockType < ' hir > {
57
74
/// An `if x.is_xxx() { a } else { b } ` expression.
@@ -78,6 +95,29 @@ enum IfBlockType<'hir> {
78
95
) ,
79
96
}
80
97
98
+ fn check_let_some_else_return_none ( cx : & LateContext < ' _ > , stmt : & Stmt < ' _ > ) {
99
+ if let StmtKind :: Local ( Local { pat, init : Some ( init_expr) , els : Some ( els) , .. } ) = stmt. kind &&
100
+ let Block { stmts : & [ ] , expr : Some ( els) , .. } = els &&
101
+ let Some ( inner_pat) = pat_and_expr_can_be_question_mark ( cx, pat, els)
102
+ {
103
+ let mut applicability = Applicability :: MaybeIncorrect ;
104
+ let init_expr_str = snippet_with_applicability ( cx, init_expr. span , ".." , & mut applicability) ;
105
+ let receiver_str = snippet_with_applicability ( cx, inner_pat. span , ".." , & mut applicability) ;
106
+ let sugg = format ! (
107
+ "let {receiver_str} = {init_expr_str}?;" ,
108
+ ) ;
109
+ span_lint_and_sugg (
110
+ cx,
111
+ QUESTION_MARK ,
112
+ stmt. span ,
113
+ "this `let...else` may be rewritten with the `?` operator" ,
114
+ "replace it with" ,
115
+ sugg,
116
+ applicability,
117
+ ) ;
118
+ }
119
+ }
120
+
81
121
fn is_early_return ( smbl : Symbol , cx : & LateContext < ' _ > , if_block : & IfBlockType < ' _ > ) -> bool {
82
122
match * if_block {
83
123
IfBlockType :: IfIs ( caller, caller_ty, call_sym, if_then, _) => {
@@ -259,6 +299,12 @@ fn is_try_block(cx: &LateContext<'_>, bl: &rustc_hir::Block<'_>) -> bool {
259
299
}
260
300
261
301
impl < ' tcx > LateLintPass < ' tcx > for QuestionMark {
302
+ fn check_stmt ( & mut self , cx : & LateContext < ' tcx > , stmt : & ' tcx Stmt < ' _ > ) {
303
+ if !in_constant ( cx, stmt. hir_id ) {
304
+ check_let_some_else_return_none ( cx, stmt) ;
305
+ }
306
+ self . check_manual_let_else ( cx, stmt) ;
307
+ }
262
308
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
263
309
if !in_constant ( cx, expr. hir_id ) {
264
310
self . check_is_none_or_err_and_early_return ( cx, expr) ;
@@ -291,4 +337,5 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark {
291
337
. expect ( "blocks are always part of bodies and must have a depth" ) -= 1 ;
292
338
}
293
339
}
340
+ extract_msrv_attr ! ( LateContext ) ;
294
341
}
0 commit comments