1
1
use if_chain:: if_chain;
2
2
use rustc_errors:: Applicability ;
3
3
use rustc_hir:: def:: { DefKind , Res } ;
4
- use rustc_hir:: { def, Block , Expr , ExprKind , StmtKind } ;
4
+ use rustc_hir:: { def, BindingAnnotation , Block , Expr , ExprKind , MatchSource , PatKind , StmtKind } ;
5
5
use rustc_lint:: { LateContext , LateLintPass } ;
6
6
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
7
7
8
8
use crate :: utils:: paths:: { OPTION , OPTION_NONE } ;
9
9
use crate :: utils:: sugg:: Sugg ;
10
- use crate :: utils:: { higher, match_def_path, match_type, span_lint_and_then, SpanlessEq } ;
10
+ use crate :: utils:: {
11
+ higher, match_def_path, match_qpath, match_type, snippet_with_applicability, span_lint_and_sugg, SpanlessEq ,
12
+ } ;
11
13
12
14
declare_clippy_lint ! {
13
15
/// **What it does:** Checks for expressions that could be replaced by the question mark operator.
@@ -55,7 +57,8 @@ impl QuestionMark {
55
57
if Self :: is_option( cx, subject) ;
56
58
57
59
then {
58
- let receiver_str = & Sugg :: hir( cx, subject, ".." ) ;
60
+ let mut applicability = Applicability :: MachineApplicable ;
61
+ let receiver_str = & Sugg :: hir_with_applicability( cx, subject, ".." , & mut applicability) ;
59
62
let mut replacement: Option <String > = None ;
60
63
if let Some ( else_) = else_ {
61
64
if_chain! {
@@ -74,25 +77,61 @@ impl QuestionMark {
74
77
}
75
78
76
79
if let Some ( replacement_str) = replacement {
77
- span_lint_and_then (
80
+ span_lint_and_sugg (
78
81
cx,
79
82
QUESTION_MARK ,
80
83
expr. span,
81
84
"this block may be rewritten with the `?` operator" ,
82
- |db| {
83
- db. span_suggestion(
84
- expr. span,
85
- "replace_it_with" ,
86
- replacement_str,
87
- Applicability :: MaybeIncorrect , // snippet
88
- ) ;
89
- }
85
+ "replace it with" ,
86
+ replacement_str,
87
+ applicability,
90
88
)
91
89
}
92
90
}
93
91
}
94
92
}
95
93
94
+ fn check_if_let_some_and_early_return_none ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > ) {
95
+ if_chain ! {
96
+ if let ExprKind :: Match ( subject, arms, source) = & expr. kind;
97
+ if * source == MatchSource :: IfLetDesugar { contains_else_clause: true } ;
98
+ if Self :: is_option( cx, subject) ;
99
+
100
+ if let PatKind :: TupleStruct ( path1, fields, None ) = & arms[ 0 ] . pat. kind;
101
+ if match_qpath( path1, & [ "Some" ] ) ;
102
+ if let PatKind :: Binding ( annot, _, bind, _) = & fields[ 0 ] . kind;
103
+ let by_ref = matches!( annot, BindingAnnotation :: Ref | BindingAnnotation :: RefMut ) ;
104
+
105
+ if let ExprKind :: Block ( block, None ) = & arms[ 0 ] . body. kind;
106
+ if block. stmts. is_empty( ) ;
107
+ if let Some ( trailing_expr) = & block. expr;
108
+ if let ExprKind :: Path ( path) = & trailing_expr. kind;
109
+ if match_qpath( path, & [ & bind. as_str( ) ] ) ;
110
+
111
+ if let PatKind :: Wild = arms[ 1 ] . pat. kind;
112
+ if Self :: expression_returns_none( cx, arms[ 1 ] . body) ;
113
+ then {
114
+ let mut applicability = Applicability :: MachineApplicable ;
115
+ let receiver_str = snippet_with_applicability( cx, subject. span, ".." , & mut applicability) ;
116
+ let replacement = format!(
117
+ "{}{}?" ,
118
+ receiver_str,
119
+ if by_ref { ".as_ref()" } else { "" } ,
120
+ ) ;
121
+
122
+ span_lint_and_sugg(
123
+ cx,
124
+ QUESTION_MARK ,
125
+ expr. span,
126
+ "this if-let-else may be rewritten with the `?` operator" ,
127
+ "replace it with" ,
128
+ replacement,
129
+ applicability,
130
+ )
131
+ }
132
+ }
133
+ }
134
+
96
135
fn moves_by_default ( cx : & LateContext < ' _ , ' _ > , expression : & Expr < ' _ > ) -> bool {
97
136
let expr_ty = cx. tables . expr_ty ( expression) ;
98
137
@@ -158,5 +197,6 @@ impl QuestionMark {
158
197
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for QuestionMark {
159
198
fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr < ' _ > ) {
160
199
Self :: check_is_none_and_early_return_none ( cx, expr) ;
200
+ Self :: check_if_let_some_and_early_return_none ( cx, expr) ;
161
201
}
162
202
}
0 commit comments