@@ -2,7 +2,7 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
22use clippy_utils:: diagnostics:: span_lint_and_sugg;
33use clippy_utils:: higher:: IfLetOrMatch ;
44use 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 } ;
66use clippy_utils:: {
77 can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id,
88 peel_hir_expr_refs, peel_hir_expr_while, CaptureKind ,
@@ -11,7 +11,8 @@ use rustc_ast::util::parser::PREC_POSTFIX;
1111use rustc_errors:: Applicability ;
1212use rustc_hir:: LangItem :: { OptionNone , OptionSome } ;
1313use 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 ,
1516} ;
1617use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
1718use rustc_middle:: lint:: in_external_macro;
@@ -93,20 +94,20 @@ impl LateLintPass<'_> for ManualMap {
9394 return ;
9495 }
9596
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) {
9798 Some ( expr) => expr,
9899 None => return ,
99100 } ;
100101
101102 // 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
103104 && !is_lint_allowed ( cx, OPTION_MAP_UNIT_FN , expr. hir_id )
104105 {
105106 return ;
106107 }
107108
108109 // `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 ( ) {
110111 return ;
111112 }
112113
@@ -120,7 +121,7 @@ impl LateLintPass<'_> for ManualMap {
120121 None => "" ,
121122 } ;
122123
123- match can_move_expr_to_closure ( cx, some_expr) {
124+ match can_move_expr_to_closure ( cx, some_expr. expr ) {
124125 Some ( captures) => {
125126 // Check if captures the closure will need conflict with borrows made in the scrutinee.
126127 // TODO: check all the references made in the scrutinee expression. This will require interacting
@@ -158,12 +159,14 @@ impl LateLintPass<'_> for ManualMap {
158159 } ;
159160
160161 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 {
163167 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)
167170 && !is_lint_allowed( cx, MATCH_AS_REF , expr. hir_id)
168171 && binding_ref. is_some( )
169172 {
@@ -176,21 +179,38 @@ impl LateLintPass<'_> for ManualMap {
176179 } else {
177180 ""
178181 } ;
179- format ! (
180- "|{}{}| {}" ,
181- annotation,
182- some_binding,
183- snippet_with_context( cx, some_expr. span, expr_ctxt, ".." , & mut app) . 0
184- )
185- } ,
182+ if some_expr. needs_unsafe_block {
183+ format!(
184+ "|{}{}| unsafe {{ {} }}" ,
185+ annotation,
186+ some_binding,
187+ snippet_with_context( cx, some_expr. expr. span, expr_ctxt, ".." , & mut app) . 0
188+ )
189+ } else {
190+ format!(
191+ "|{}{}| {}" ,
192+ annotation,
193+ some_binding,
194+ snippet_with_context( cx, some_expr. expr. span, expr_ctxt, ".." , & mut app) . 0
195+ )
196+ }
197+ }
186198 }
187199 } else if !is_wild_none && explicit_ref. is_none ( ) {
188200 // 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- )
201+ if some_expr. needs_unsafe_block {
202+ format ! (
203+ "|{}| unsafe {{ {} }}" ,
204+ snippet_with_context( cx, some_pat. span, expr_ctxt, ".." , & mut app) . 0 ,
205+ snippet_with_context( cx, some_expr. expr. span, expr_ctxt, ".." , & mut app) . 0
206+ )
207+ } else {
208+ format ! (
209+ "|{}| {}" ,
210+ snippet_with_context( cx, some_pat. span, expr_ctxt, ".." , & mut app) . 0 ,
211+ snippet_with_context( cx, some_expr. expr. span, expr_ctxt, ".." , & mut app) . 0
212+ )
213+ }
194214 } else {
195215 // Refutable bindings and mixed reference annotations can't be handled by `map`.
196216 return ;
@@ -217,7 +237,9 @@ impl LateLintPass<'_> for ManualMap {
217237fn can_pass_as_func ( cx : & LateContext < ' tcx > , binding : HirId , expr : & ' tcx Expr < ' _ > ) -> Option < & ' tcx Expr < ' tcx > > {
218238 match expr. kind {
219239 ExprKind :: Call ( func, [ arg] )
220- if path_to_local_id ( arg, binding) && cx. typeck_results ( ) . expr_adjustments ( arg) . is_empty ( ) =>
240+ if path_to_local_id ( arg, binding)
241+ && cx. typeck_results ( ) . expr_adjustments ( arg) . is_empty ( )
242+ && !type_is_unsafe_function ( cx, cx. typeck_results ( ) . expr_ty ( func) . peel_refs ( ) ) =>
221243 {
222244 Some ( func)
223245 } ,
@@ -237,6 +259,11 @@ enum OptionPat<'a> {
237259 } ,
238260}
239261
262+ struct SomeExpr < ' tcx > {
263+ expr : & ' tcx Expr < ' tcx > ,
264+ needs_unsafe_block : bool ,
265+ }
266+
240267// Try to parse into a recognized `Option` pattern.
241268// i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
242269fn try_parse_pattern ( cx : & LateContext < ' tcx > , pat : & ' tcx Pat < ' _ > , ctxt : SyntaxContext ) -> Option < OptionPat < ' tcx > > {
@@ -257,7 +284,12 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxCon
257284}
258285
259286// 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 > > {
287+ fn get_some_expr (
288+ cx : & LateContext < ' tcx > ,
289+ expr : & ' tcx Expr < ' _ > ,
290+ needs_unsafe_block : bool ,
291+ ctxt : SyntaxContext ,
292+ ) -> Option < SomeExpr < ' tcx > > {
261293 // TODO: Allow more complex expressions.
262294 match expr. kind {
263295 ExprKind :: Call (
@@ -266,15 +298,24 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte
266298 ..
267299 } ,
268300 [ arg] ,
269- ) if ctxt == expr. span . ctxt ( ) && is_lang_ctor ( cx, qpath, OptionSome ) => Some ( arg) ,
301+ ) if ctxt == expr. span . ctxt ( ) && is_lang_ctor ( cx, qpath, OptionSome ) => Some ( SomeExpr {
302+ expr : arg,
303+ needs_unsafe_block,
304+ } ) ,
270305 ExprKind :: Block (
271306 Block {
272307 stmts : [ ] ,
273308 expr : Some ( expr) ,
309+ rules,
274310 ..
275311 } ,
276312 _,
277- ) => get_some_expr ( cx, expr, ctxt) ,
313+ ) => get_some_expr (
314+ cx,
315+ expr,
316+ needs_unsafe_block || * rules == BlockCheckMode :: UnsafeBlock ( UnsafeSource :: UserProvided ) ,
317+ ctxt,
318+ ) ,
278319 _ => None ,
279320 }
280321}
0 commit comments