33use clippy_utils:: diagnostics:: { span_lint, span_lint_and_sugg, span_lint_and_then} ;
44use clippy_utils:: source:: snippet_opt;
55use clippy_utils:: ty:: expr_sig;
6+ use clippy_utils:: visitors:: contains_unsafe_block;
67use clippy_utils:: { get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths} ;
78use if_chain:: if_chain;
89use rustc_errors:: Applicability ;
910use rustc_hir:: def_id:: DefId ;
1011use rustc_hir:: hir_id:: HirIdMap ;
1112use rustc_hir:: intravisit:: { walk_expr, Visitor } ;
1213use rustc_hir:: {
13- self as hir, AnonConst , BinOpKind , BindingAnnotation , Body , Expr , ExprKind , FnDecl , FnRetTy , GenericArg ,
14+ self as hir, AnonConst , BinOpKind , BindingAnnotation , Body , Expr , ExprKind , FnRetTy , FnSig , GenericArg ,
1415 ImplItemKind , ItemKind , Lifetime , LifetimeName , Mutability , Node , Param , ParamName , PatKind , QPath , TraitFn ,
15- TraitItem , TraitItemKind , TyKind ,
16+ TraitItem , TraitItemKind , TyKind , Unsafety ,
1617} ;
1718use rustc_lint:: { LateContext , LateLintPass } ;
1819use rustc_middle:: hir:: nested_filter;
@@ -145,7 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
145146 return ;
146147 }
147148
148- check_mut_from_ref ( cx, sig. decl ) ;
149+ check_mut_from_ref ( cx, sig, None ) ;
149150 for arg in check_fn_args (
150151 cx,
151152 cx. tcx . fn_sig ( item. def_id ) . skip_binder ( ) . inputs ( ) ,
@@ -170,10 +171,10 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
170171 fn check_body ( & mut self , cx : & LateContext < ' tcx > , body : & ' tcx Body < ' _ > ) {
171172 let hir = cx. tcx . hir ( ) ;
172173 let mut parents = hir. parent_iter ( body. value . hir_id ) ;
173- let ( item_id, decl , is_trait_item) = match parents. next ( ) {
174+ let ( item_id, sig , is_trait_item) = match parents. next ( ) {
174175 Some ( ( _, Node :: Item ( i) ) ) => {
175176 if let ItemKind :: Fn ( sig, ..) = & i. kind {
176- ( i. def_id , sig. decl , false )
177+ ( i. def_id , sig, false )
177178 } else {
178179 return ;
179180 }
@@ -185,22 +186,23 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
185186 return ;
186187 }
187188 if let ImplItemKind :: Fn ( sig, _) = & i. kind {
188- ( i. def_id , sig. decl , false )
189+ ( i. def_id , sig, false )
189190 } else {
190191 return ;
191192 }
192193 } ,
193194 Some ( ( _, Node :: TraitItem ( i) ) ) => {
194195 if let TraitItemKind :: Fn ( sig, _) = & i. kind {
195- ( i. def_id , sig. decl , true )
196+ ( i. def_id , sig, true )
196197 } else {
197198 return ;
198199 }
199200 } ,
200201 _ => return ,
201202 } ;
202203
203- check_mut_from_ref ( cx, decl) ;
204+ check_mut_from_ref ( cx, sig, Some ( body) ) ;
205+ let decl = sig. decl ;
204206 let sig = cx. tcx . fn_sig ( item_id) . skip_binder ( ) ;
205207 let lint_args: Vec < _ > = check_fn_args ( cx, sig. inputs ( ) , decl. inputs , body. params )
206208 . filter ( |arg| !is_trait_item || arg. mutability ( ) == Mutability :: Not )
@@ -478,31 +480,31 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
478480 } )
479481}
480482
481- fn check_mut_from_ref ( cx : & LateContext < ' _ > , decl : & FnDecl < ' _ > ) {
482- if let FnRetTy :: Return ( ty) = decl. output {
483- if let Some ( ( out, Mutability :: Mut , _) ) = get_rptr_lm ( ty) {
484- let mut immutables = vec ! [ ] ;
485- for ( _ , mutbl , argspan ) in decl
486- . inputs
487- . iter ( )
488- . filter_map ( get_rptr_lm )
489- . filter ( | & ( lt , _ , _ ) | lt . name == out . name )
490- {
491- if mutbl == Mutability :: Mut {
492- return ;
493- }
494- immutables . push ( argspan ) ;
495- }
496- if immutables . is_empty ( ) {
497- return ;
498- }
483+ fn check_mut_from_ref < ' tcx > ( cx : & LateContext < ' tcx > , sig : & FnSig < ' _ > , body : Option < & ' tcx Body < ' _ > > ) {
484+ if let FnRetTy :: Return ( ty) = sig . decl . output
485+ && let Some ( ( out, Mutability :: Mut , _) ) = get_rptr_lm ( ty)
486+ {
487+ let args : Option < Vec < _ > > = sig
488+ . decl
489+ . inputs
490+ . iter ( )
491+ . filter_map ( get_rptr_lm )
492+ . filter ( | & ( lt , _ , _ ) | lt . name == out . name )
493+ . map ( | ( _ , mutability , span ) | ( mutability == Mutability :: Not ) . then ( || span ) )
494+ . collect ( ) ;
495+ if let Some ( args ) = args
496+ && !args . is_empty ( )
497+ && body . map_or ( true , |body| {
498+ sig . header . unsafety == Unsafety :: Unsafe || contains_unsafe_block ( cx , & body . value )
499+ } )
500+ {
499501 span_lint_and_then (
500502 cx,
501503 MUT_FROM_REF ,
502504 ty. span ,
503505 "mutable borrow from immutable input(s)" ,
504506 |diag| {
505- let ms = MultiSpan :: from_spans ( immutables ) ;
507+ let ms = MultiSpan :: from_spans ( args ) ;
506508 diag. span_note ( ms, "immutable borrow here" ) ;
507509 } ,
508510 ) ;
0 commit comments