1
1
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
2
use clippy_utils:: higher;
3
3
use clippy_utils:: source:: snippet_with_applicability;
4
- use clippy_utils:: sugg:: Sugg ;
5
4
use clippy_utils:: ty:: is_type_diagnostic_item;
6
5
use clippy_utils:: { eq_expr_value, is_lang_ctor, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt} ;
7
6
use if_chain:: if_chain;
8
7
use rustc_errors:: Applicability ;
9
8
use rustc_hir:: LangItem :: { OptionNone , OptionSome , ResultOk } ;
10
9
use rustc_hir:: { BindingAnnotation , Expr , ExprKind , PatKind } ;
11
10
use rustc_lint:: { LateContext , LateLintPass } ;
11
+ use rustc_middle:: ty:: Ty ;
12
12
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
13
- use rustc_span:: sym;
13
+ use rustc_span:: { sym, symbol :: Symbol } ;
14
14
15
15
declare_clippy_lint ! {
16
16
/// ### What it does
@@ -60,35 +60,25 @@ impl QuestionMark {
60
60
if let Some ( higher:: If { cond, then, r#else } ) = higher:: If :: hir( expr) ;
61
61
if let ExprKind :: MethodCall ( segment, args, _) = & cond. kind;
62
62
if let Some ( subject) = args. get( 0 ) ;
63
- if ( Self :: option_check_and_early_return( cx, subject, then) && segment. ident. name == sym!( is_none) ) ||
64
- ( Self :: result_check_and_early_return( cx, subject, then) && segment. ident. name == sym!( is_err) ) ;
63
+ let subject_ty = cx. typeck_results( ) . expr_ty( subject) ;
64
+ if ( Self :: check_early_return( cx, subject, then, subject_ty, sym:: Option ) &&
65
+ segment. ident. name == sym!( is_none) ) ||
66
+ ( Self :: check_early_return( cx, subject, then, subject_ty, sym:: Result ) &&
67
+ segment. ident. name == sym!( is_err) ) ;
65
68
then {
66
69
let mut applicability = Applicability :: MachineApplicable ;
67
- let receiver_str = & Sugg :: hir_with_applicability( cx, subject, ".." , & mut applicability) ;
68
- let mut replacement: Option <String > = None ;
70
+ let receiver_str = snippet_with_applicability( cx, subject. span, ".." , & mut applicability) ;
71
+ let by_ref = !subject_ty. is_copy_modulo_regions( cx. tcx. at( subject. span) , cx. param_env) &&
72
+ !matches!( subject. kind, ExprKind :: Call ( ..) | ExprKind :: MethodCall ( ..) ) ;
73
+ let mut suggestion = String :: new( ) ;
69
74
if let Some ( else_inner) = r#else {
70
75
if eq_expr_value( cx, subject, peel_blocks( else_inner) ) {
71
- replacement = Some ( format!( "Some({}?)" , receiver_str) ) ;
76
+ suggestion = format!( "Some({}?)" , receiver_str) ;
72
77
}
73
- } else if Self :: moves_by_default( cx, subject)
74
- && !matches!( subject. kind, ExprKind :: Call ( ..) | ExprKind :: MethodCall ( ..) )
75
- {
76
- replacement = Some ( format!( "{}.as_ref()?;" , receiver_str) ) ;
77
78
} else {
78
- replacement = Some ( format!( "{}?;" , receiver_str) ) ;
79
- }
80
-
81
- if let Some ( replacement_str) = replacement {
82
- span_lint_and_sugg(
83
- cx,
84
- QUESTION_MARK ,
85
- expr. span,
86
- "this block may be rewritten with the `?` operator" ,
87
- "replace it with" ,
88
- replacement_str,
89
- applicability,
90
- ) ;
79
+ suggestion = format!( "{}{}?;" , receiver_str, if by_ref { ".as_ref()" } else { "" } ) ;
91
80
}
81
+ Self :: offer_suggestion( cx, expr, suggestion, applicability) ;
92
82
}
93
83
}
94
84
}
@@ -98,8 +88,11 @@ impl QuestionMark {
98
88
if let Some ( higher:: IfLet { let_pat, let_expr, if_then, if_else: Some ( if_else) } )
99
89
= higher:: IfLet :: hir( cx, expr) ;
100
90
if let PatKind :: TupleStruct ( ref path1, fields, None ) = let_pat. kind;
101
- if ( Self :: option_check_and_early_return( cx, let_expr, if_else) && is_lang_ctor( cx, path1, OptionSome ) ) ||
102
- ( Self :: result_check_and_early_return( cx, let_expr, if_else) && is_lang_ctor( cx, path1, ResultOk ) ) ;
91
+ let let_expr_ty = cx. typeck_results( ) . expr_ty( let_expr) ;
92
+ if ( Self :: check_early_return( cx, let_expr, if_else, let_expr_ty, sym:: Option ) &&
93
+ is_lang_ctor( cx, path1, OptionSome ) ) ||
94
+ ( Self :: check_early_return( cx, let_expr, if_else, let_expr_ty, sym:: Result ) &&
95
+ is_lang_ctor( cx, path1, ResultOk ) ) ;
103
96
104
97
if let PatKind :: Binding ( annot, bind_id, _, _) = fields[ 0 ] . kind;
105
98
let by_ref = matches!( annot, BindingAnnotation :: Ref | BindingAnnotation :: RefMut ) ;
@@ -108,59 +101,52 @@ impl QuestionMark {
108
101
let mut applicability = Applicability :: MachineApplicable ;
109
102
let receiver_str = snippet_with_applicability( cx, let_expr. span, ".." , & mut applicability) ;
110
103
let replacement = format!( "{}{}?" , receiver_str, if by_ref { ".as_ref()" } else { "" } , ) ;
111
-
112
- span_lint_and_sugg(
113
- cx,
114
- QUESTION_MARK ,
115
- expr. span,
116
- "this if-let-else may be rewritten with the `?` operator" ,
117
- "replace it with" ,
118
- replacement,
119
- applicability,
120
- ) ;
104
+ Self :: offer_suggestion( cx, expr, replacement, applicability) ;
121
105
}
122
106
}
123
107
}
124
108
125
- fn result_check_and_early_return ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , nested_expr : & Expr < ' _ > ) -> bool {
126
- Self :: is_result ( cx, expr) && Self :: expression_returns_unmodified_err ( cx, nested_expr, expr)
127
- }
128
-
129
- fn option_check_and_early_return ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , nested_expr : & Expr < ' _ > ) -> bool {
130
- Self :: is_option ( cx, expr) && Self :: expression_returns_none ( cx, nested_expr)
131
- }
132
-
133
- fn moves_by_default ( cx : & LateContext < ' _ > , expression : & Expr < ' _ > ) -> bool {
134
- let expr_ty = cx. typeck_results ( ) . expr_ty ( expression) ;
135
-
136
- !expr_ty. is_copy_modulo_regions ( cx. tcx . at ( expression. span ) , cx. param_env )
109
+ fn check_early_return (
110
+ cx : & LateContext < ' _ > ,
111
+ cond_expr : & Expr < ' _ > ,
112
+ nested_expr : & Expr < ' _ > ,
113
+ cond_ty : Ty < ' _ > ,
114
+ ret_sym : Symbol ,
115
+ ) -> bool {
116
+ is_type_diagnostic_item ( cx, cond_ty, ret_sym)
117
+ && Self :: expr_return_none_or_err ( cx, cond_expr, nested_expr, ret_sym)
137
118
}
138
119
139
- fn is_option ( cx : & LateContext < ' _ > , expression : & Expr < ' _ > ) -> bool {
140
- let expr_ty = cx. typeck_results ( ) . expr_ty ( expression) ;
141
-
142
- is_type_diagnostic_item ( cx, expr_ty, sym:: Option )
143
- }
144
-
145
- fn is_result ( cx : & LateContext < ' _ > , expression : & Expr < ' _ > ) -> bool {
146
- let expr_ty = cx. typeck_results ( ) . expr_ty ( expression) ;
147
-
148
- is_type_diagnostic_item ( cx, expr_ty, sym:: Result )
149
- }
150
-
151
- fn expression_returns_none ( cx : & LateContext < ' _ > , expression : & Expr < ' _ > ) -> bool {
152
- match peel_blocks_with_stmt ( expression) . kind {
153
- ExprKind :: Ret ( Some ( expr) ) => Self :: expression_returns_none ( cx, expr) ,
154
- ExprKind :: Path ( ref qpath) => is_lang_ctor ( cx, qpath, OptionNone ) ,
120
+ fn expr_return_none_or_err (
121
+ cx : & LateContext < ' _ > ,
122
+ cond_expr : & Expr < ' _ > ,
123
+ nested_expr : & Expr < ' _ > ,
124
+ ret_sym : Symbol ,
125
+ ) -> bool {
126
+ match peel_blocks_with_stmt ( nested_expr) . kind {
127
+ ExprKind :: Ret ( Some ( ret_expr) ) => Self :: expr_return_none_or_err ( cx, cond_expr, ret_expr, ret_sym) ,
128
+ ExprKind :: Path ( ref qpath) => match ret_sym {
129
+ sym:: Option => is_lang_ctor ( cx, qpath, OptionNone ) ,
130
+ sym:: Result => {
131
+ path_to_local ( nested_expr) . is_some ( ) && path_to_local ( nested_expr) == path_to_local ( cond_expr)
132
+ } ,
133
+ _ => false ,
134
+ } ,
155
135
_ => false ,
156
136
}
157
137
}
158
138
159
- fn expression_returns_unmodified_err ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , cond_expr : & Expr < ' _ > ) -> bool {
160
- match peel_blocks_with_stmt ( expr) . kind {
161
- ExprKind :: Ret ( Some ( ret_expr) ) => Self :: expression_returns_unmodified_err ( cx, ret_expr, cond_expr) ,
162
- ExprKind :: Path ( _) => path_to_local ( expr) . is_some ( ) && path_to_local ( expr) == path_to_local ( cond_expr) ,
163
- _ => false ,
139
+ fn offer_suggestion ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , suggestion : String , applicability : Applicability ) {
140
+ if !suggestion. is_empty ( ) {
141
+ span_lint_and_sugg (
142
+ cx,
143
+ QUESTION_MARK ,
144
+ expr. span ,
145
+ "this block may be rewritten with the `?` operator" ,
146
+ "replace it with" ,
147
+ suggestion,
148
+ applicability,
149
+ ) ;
164
150
}
165
151
}
166
152
}
0 commit comments