@@ -2,7 +2,7 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
2
2
use clippy_utils:: diagnostics:: span_lint_and_sugg;
3
3
use clippy_utils:: higher:: IfLetOrMatch ;
4
4
use clippy_utils:: source:: { snippet_with_applicability, snippet_with_context} ;
5
- use clippy_utils:: ty:: { is_type_diagnostic_item, peel_mid_ty_refs_is_mutable} ;
5
+ use clippy_utils:: ty:: { is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function } ;
6
6
use clippy_utils:: {
7
7
can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id,
8
8
peel_hir_expr_refs, peel_hir_expr_while, CaptureKind ,
@@ -11,7 +11,8 @@ use rustc_ast::util::parser::PREC_POSTFIX;
11
11
use rustc_errors:: Applicability ;
12
12
use rustc_hir:: LangItem :: { OptionNone , OptionSome } ;
13
13
use rustc_hir:: {
14
- def:: Res , Arm , BindingAnnotation , Block , Expr , ExprKind , HirId , Mutability , Pat , PatKind , Path , QPath ,
14
+ def:: Res , Arm , BindingAnnotation , Block , BlockCheckMode , Expr , ExprKind , HirId , Mutability , Pat , PatKind , Path ,
15
+ QPath , UnsafeSource ,
15
16
} ;
16
17
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
17
18
use rustc_middle:: lint:: in_external_macro;
@@ -93,20 +94,20 @@ impl LateLintPass<'_> for ManualMap {
93
94
return ;
94
95
}
95
96
96
- let some_expr = match get_some_expr ( cx, some_expr, expr_ctxt) {
97
+ let some_expr = match get_some_expr ( cx, some_expr, false , expr_ctxt) {
97
98
Some ( expr) => expr,
98
99
None => return ,
99
100
} ;
100
101
101
102
// These two lints will go back and forth with each other.
102
- if cx. typeck_results ( ) . expr_ty ( some_expr) == cx. tcx . types . unit
103
+ if cx. typeck_results ( ) . expr_ty ( some_expr. expr ) == cx. tcx . types . unit
103
104
&& !is_lint_allowed ( cx, OPTION_MAP_UNIT_FN , expr. hir_id )
104
105
{
105
106
return ;
106
107
}
107
108
108
109
// `map` won't perform any adjustments.
109
- if !cx. typeck_results ( ) . expr_adjustments ( some_expr) . is_empty ( ) {
110
+ if !cx. typeck_results ( ) . expr_adjustments ( some_expr. expr ) . is_empty ( ) {
110
111
return ;
111
112
}
112
113
@@ -120,7 +121,7 @@ impl LateLintPass<'_> for ManualMap {
120
121
None => "" ,
121
122
} ;
122
123
123
- match can_move_expr_to_closure ( cx, some_expr) {
124
+ match can_move_expr_to_closure ( cx, some_expr. expr ) {
124
125
Some ( captures) => {
125
126
// Check if captures the closure will need conflict with borrows made in the scrutinee.
126
127
// TODO: check all the references made in the scrutinee expression. This will require interacting
@@ -158,12 +159,14 @@ impl LateLintPass<'_> for ManualMap {
158
159
} ;
159
160
160
161
let body_str = if let PatKind :: Binding ( annotation, id, some_binding, None ) = some_pat. kind {
161
- match can_pass_as_func ( cx, id, some_expr) {
162
- Some ( func) if func. span . ctxt ( ) == some_expr. span . ctxt ( ) => {
162
+ if_chain ! {
163
+ if !some_expr. needs_unsafe_block;
164
+ if let Some ( func) = can_pass_as_func( cx, id, some_expr. expr) ;
165
+ if func. span. ctxt( ) == some_expr. expr. span. ctxt( ) ;
166
+ then {
163
167
snippet_with_applicability( cx, func. span, ".." , & mut app) . into_owned( )
164
- } ,
165
- _ => {
166
- if path_to_local_id ( some_expr, id)
168
+ } else {
169
+ if path_to_local_id( some_expr. expr, id)
167
170
&& !is_lint_allowed( cx, MATCH_AS_REF , expr. hir_id)
168
171
&& binding_ref. is_some( )
169
172
{
@@ -176,21 +179,23 @@ impl LateLintPass<'_> for ManualMap {
176
179
} else {
177
180
""
178
181
} ;
179
- format ! (
180
- "|{}{}| {}" ,
181
- annotation,
182
- some_binding ,
183
- snippet_with_context ( cx , some_expr . span , expr_ctxt , ".." , & mut app ) . 0
184
- )
185
- } ,
182
+ let expr_snip = snippet_with_context ( cx , some_expr . expr . span , expr_ctxt , ".." , & mut app ) . 0 ;
183
+ if some_expr . needs_unsafe_block {
184
+ format! ( "|{}{}| unsafe {{ {} }}" , annotation, some_binding , expr_snip )
185
+ } else {
186
+ format! ( "|{}{}| {}" , annotation , some_binding , expr_snip )
187
+ }
188
+ }
186
189
}
187
190
} else if !is_wild_none && explicit_ref. is_none ( ) {
188
191
// TODO: handle explicit reference annotations.
189
- format ! (
190
- "|{}| {}" ,
191
- snippet_with_context( cx, some_pat. span, expr_ctxt, ".." , & mut app) . 0 ,
192
- snippet_with_context( cx, some_expr. span, expr_ctxt, ".." , & mut app) . 0
193
- )
192
+ let pat_snip = snippet_with_context ( cx, some_pat. span , expr_ctxt, ".." , & mut app) . 0 ;
193
+ let expr_snip = snippet_with_context ( cx, some_expr. expr . span , expr_ctxt, ".." , & mut app) . 0 ;
194
+ if some_expr. needs_unsafe_block {
195
+ format ! ( "|{}| unsafe {{ {} }}" , pat_snip, expr_snip)
196
+ } else {
197
+ format ! ( "|{}| {}" , pat_snip, expr_snip)
198
+ }
194
199
} else {
195
200
// Refutable bindings and mixed reference annotations can't be handled by `map`.
196
201
return ;
@@ -217,7 +222,9 @@ impl LateLintPass<'_> for ManualMap {
217
222
fn can_pass_as_func ( cx : & LateContext < ' tcx > , binding : HirId , expr : & ' tcx Expr < ' _ > ) -> Option < & ' tcx Expr < ' tcx > > {
218
223
match expr. kind {
219
224
ExprKind :: Call ( func, [ arg] )
220
- if path_to_local_id ( arg, binding) && cx. typeck_results ( ) . expr_adjustments ( arg) . is_empty ( ) =>
225
+ if path_to_local_id ( arg, binding)
226
+ && cx. typeck_results ( ) . expr_adjustments ( arg) . is_empty ( )
227
+ && !type_is_unsafe_function ( cx, cx. typeck_results ( ) . expr_ty ( func) . peel_refs ( ) ) =>
221
228
{
222
229
Some ( func)
223
230
} ,
@@ -237,6 +244,11 @@ enum OptionPat<'a> {
237
244
} ,
238
245
}
239
246
247
+ struct SomeExpr < ' tcx > {
248
+ expr : & ' tcx Expr < ' tcx > ,
249
+ needs_unsafe_block : bool ,
250
+ }
251
+
240
252
// Try to parse into a recognized `Option` pattern.
241
253
// i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
242
254
fn try_parse_pattern ( cx : & LateContext < ' tcx > , pat : & ' tcx Pat < ' _ > , ctxt : SyntaxContext ) -> Option < OptionPat < ' tcx > > {
@@ -257,7 +269,12 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxCon
257
269
}
258
270
259
271
// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
260
- fn get_some_expr ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > , ctxt : SyntaxContext ) -> Option < & ' tcx Expr < ' tcx > > {
272
+ fn get_some_expr (
273
+ cx : & LateContext < ' tcx > ,
274
+ expr : & ' tcx Expr < ' _ > ,
275
+ needs_unsafe_block : bool ,
276
+ ctxt : SyntaxContext ,
277
+ ) -> Option < SomeExpr < ' tcx > > {
261
278
// TODO: Allow more complex expressions.
262
279
match expr. kind {
263
280
ExprKind :: Call (
@@ -266,15 +283,24 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte
266
283
..
267
284
} ,
268
285
[ arg] ,
269
- ) if ctxt == expr. span . ctxt ( ) && is_lang_ctor ( cx, qpath, OptionSome ) => Some ( arg) ,
286
+ ) if ctxt == expr. span . ctxt ( ) && is_lang_ctor ( cx, qpath, OptionSome ) => Some ( SomeExpr {
287
+ expr : arg,
288
+ needs_unsafe_block,
289
+ } ) ,
270
290
ExprKind :: Block (
271
291
Block {
272
292
stmts : [ ] ,
273
293
expr : Some ( expr) ,
294
+ rules,
274
295
..
275
296
} ,
276
297
_,
277
- ) => get_some_expr ( cx, expr, ctxt) ,
298
+ ) => get_some_expr (
299
+ cx,
300
+ expr,
301
+ needs_unsafe_block || * rules == BlockCheckMode :: UnsafeBlock ( UnsafeSource :: UserProvided ) ,
302
+ ctxt,
303
+ ) ,
278
304
_ => None ,
279
305
}
280
306
}
0 commit comments