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 , MultiSpan } ;
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;
@@ -88,19 +89,26 @@ declare_clippy_lint! {
8889
8990declare_clippy_lint ! {
9091 /// ### What it does
91- /// This lint checks for functions that take immutable
92- /// references and return mutable ones.
92+ /// This lint checks for functions that take immutable references and return
93+ /// mutable ones. This will not trigger if no unsafe code exists as there
94+ /// are multiple safe functions which will do this transformation
95+ ///
96+ /// To be on the conservative side, if there's at least one mutable
97+ /// reference with the output lifetime, this lint will not trigger.
9398 ///
9499 /// ### Why is this bad?
95- /// This is trivially unsound, as one can create two
96- /// mutable references from the same (immutable!) source.
97- /// This [error](https://github.com/rust-lang/rust/issues/39465)
98- /// actually lead to an interim Rust release 1.15.1.
100+ /// Creating a mutable reference which can be repeatably derived from an
101+ /// immutable reference is unsound as it allows creating multiple live
102+ /// mutable references to the same object.
103+ ///
104+ /// This [error](https://github.com/rust-lang/rust/issues/39465) actually
105+ /// lead to an interim Rust release 1.15.1.
99106 ///
100107 /// ### Known problems
101- /// To be on the conservative side, if there's at least one
102- /// mutable reference with the output lifetime, this lint will not trigger.
103- /// In practice, this case is unlikely anyway.
108+ /// This pattern is used by memory allocators to allow allocating multiple
109+ /// objects while returning mutable references to each one. So long as
110+ /// different mutable references are returned each time such a function may
111+ /// be safe.
104112 ///
105113 /// ### Example
106114 /// ```ignore
@@ -145,7 +153,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
145153 return ;
146154 }
147155
148- check_mut_from_ref ( cx, sig. decl ) ;
156+ check_mut_from_ref ( cx, sig, None ) ;
149157 for arg in check_fn_args (
150158 cx,
151159 cx. tcx . fn_sig ( item. def_id ) . skip_binder ( ) . inputs ( ) ,
@@ -170,10 +178,10 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
170178 fn check_body ( & mut self , cx : & LateContext < ' tcx > , body : & ' tcx Body < ' _ > ) {
171179 let hir = cx. tcx . hir ( ) ;
172180 let mut parents = hir. parent_iter ( body. value . hir_id ) ;
173- let ( item_id, decl , is_trait_item) = match parents. next ( ) {
181+ let ( item_id, sig , is_trait_item) = match parents. next ( ) {
174182 Some ( ( _, Node :: Item ( i) ) ) => {
175183 if let ItemKind :: Fn ( sig, ..) = & i. kind {
176- ( i. def_id , sig. decl , false )
184+ ( i. def_id , sig, false )
177185 } else {
178186 return ;
179187 }
@@ -185,22 +193,23 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
185193 return ;
186194 }
187195 if let ImplItemKind :: Fn ( sig, _) = & i. kind {
188- ( i. def_id , sig. decl , false )
196+ ( i. def_id , sig, false )
189197 } else {
190198 return ;
191199 }
192200 } ,
193201 Some ( ( _, Node :: TraitItem ( i) ) ) => {
194202 if let TraitItemKind :: Fn ( sig, _) = & i. kind {
195- ( i. def_id , sig. decl , true )
203+ ( i. def_id , sig, true )
196204 } else {
197205 return ;
198206 }
199207 } ,
200208 _ => return ,
201209 } ;
202210
203- check_mut_from_ref ( cx, decl) ;
211+ check_mut_from_ref ( cx, sig, Some ( body) ) ;
212+ let decl = sig. decl ;
204213 let sig = cx. tcx . fn_sig ( item_id) . skip_binder ( ) ;
205214 let lint_args: Vec < _ > = check_fn_args ( cx, sig. inputs ( ) , decl. inputs , body. params )
206215 . filter ( |arg| !is_trait_item || arg. mutability ( ) == Mutability :: Not )
@@ -473,31 +482,31 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
473482 } )
474483}
475484
476- fn check_mut_from_ref ( cx : & LateContext < ' _ > , decl : & FnDecl < ' _ > ) {
477- if let FnRetTy :: Return ( ty) = decl. output {
478- if let Some ( ( out, Mutability :: Mut , _) ) = get_rptr_lm ( ty) {
479- let mut immutables = vec ! [ ] ;
480- for ( _ , mutbl , argspan ) in decl
481- . inputs
482- . iter ( )
483- . filter_map ( get_rptr_lm )
484- . filter ( | & ( lt , _ , _ ) | lt . name == out . name )
485- {
486- if mutbl == Mutability :: Mut {
487- return ;
488- }
489- immutables . push ( argspan ) ;
490- }
491- if immutables . is_empty ( ) {
492- return ;
493- }
485+ fn check_mut_from_ref < ' tcx > ( cx : & LateContext < ' tcx > , sig : & FnSig < ' _ > , body : Option < & ' tcx Body < ' _ > > ) {
486+ if let FnRetTy :: Return ( ty) = sig . decl . output
487+ && let Some ( ( out, Mutability :: Mut , _) ) = get_rptr_lm ( ty)
488+ {
489+ let args : Option < Vec < _ > > = sig
490+ . decl
491+ . inputs
492+ . iter ( )
493+ . filter_map ( get_rptr_lm )
494+ . filter ( | & ( lt , _ , _ ) | lt . name == out . name )
495+ . map ( | ( _ , mutability , span ) | ( mutability == Mutability :: Not ) . then ( || span ) )
496+ . collect ( ) ;
497+ if let Some ( args ) = args
498+ && !args . is_empty ( )
499+ && body . map_or ( true , |body| {
500+ sig . header . unsafety == Unsafety :: Unsafe || contains_unsafe_block ( cx , & body . value )
501+ } )
502+ {
494503 span_lint_and_then (
495504 cx,
496505 MUT_FROM_REF ,
497506 ty. span ,
498507 "mutable borrow from immutable input(s)" ,
499508 |diag| {
500- let ms = MultiSpan :: from_spans ( immutables ) ;
509+ let ms = MultiSpan :: from_spans ( args ) ;
501510 diag. span_note ( ms, "immutable borrow here" ) ;
502511 } ,
503512 ) ;
0 commit comments