@@ -3,17 +3,20 @@ use std::iter;
33
44use clippy_utils:: diagnostics:: span_lint_and_sugg;
55use clippy_utils:: source:: snippet;
6- use clippy_utils:: ty:: is_copy;
6+ use clippy_utils:: ty:: { for_each_top_level_late_bound_region , is_copy} ;
77use clippy_utils:: { is_self, is_self_ty} ;
8+ use core:: ops:: ControlFlow ;
89use if_chain:: if_chain;
910use rustc_ast:: attr;
11+ use rustc_data_structures:: fx:: FxHashSet ;
1012use rustc_errors:: Applicability ;
1113use rustc_hir as hir;
1214use rustc_hir:: intravisit:: FnKind ;
1315use rustc_hir:: { BindingAnnotation , Body , FnDecl , HirId , Impl , ItemKind , MutTy , Mutability , Node , PatKind } ;
1416use rustc_lint:: { LateContext , LateLintPass } ;
15- use rustc_middle:: ty;
17+ use rustc_middle:: ty:: adjustment :: { Adjust , PointerCast } ;
1618use rustc_middle:: ty:: layout:: LayoutOf ;
19+ use rustc_middle:: ty:: { self , RegionKind } ;
1720use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
1821use rustc_span:: def_id:: LocalDefId ;
1922use rustc_span:: { sym, Span } ;
@@ -141,50 +144,76 @@ impl<'tcx> PassByRefOrValue {
141144 }
142145
143146 let fn_sig = cx. tcx . fn_sig ( def_id) ;
144- let fn_sig = cx. tcx . erase_late_bound_regions ( fn_sig) ;
145-
146147 let fn_body = cx. enclosing_body . map ( |id| cx. tcx . hir ( ) . body ( id) ) ;
147148
148- for ( index, ( input, & ty) ) in iter:: zip ( decl. inputs , fn_sig. inputs ( ) ) . enumerate ( ) {
149+ // Gather all the lifetimes found in the output type which may affect whether
150+ // `TRIVIALLY_COPY_PASS_BY_REF` should be linted.
151+ let mut output_regions = FxHashSet :: default ( ) ;
152+ for_each_top_level_late_bound_region ( fn_sig. skip_binder ( ) . output ( ) , |region| -> ControlFlow < !> {
153+ output_regions. insert ( region) ;
154+ ControlFlow :: Continue ( ( ) )
155+ } ) ;
156+
157+ for ( index, ( input, ty) ) in iter:: zip (
158+ decl. inputs ,
159+ fn_sig. skip_binder ( ) . inputs ( ) . iter ( ) . map ( |& ty| fn_sig. rebind ( ty) ) ,
160+ )
161+ . enumerate ( )
162+ {
149163 // All spans generated from a proc-macro invocation are the same...
150164 match span {
151- Some ( s) if s == input. span => return ,
165+ Some ( s) if s == input. span => continue ,
152166 _ => ( ) ,
153167 }
154168
155- match ty. kind ( ) {
156- ty:: Ref ( input_lt, ty, Mutability :: Not ) => {
157- // Use lifetimes to determine if we're returning a reference to the
158- // argument. In that case we can't switch to pass-by-value as the
159- // argument will not live long enough.
160- let output_lts = match * fn_sig. output ( ) . kind ( ) {
161- ty:: Ref ( output_lt, _, _) => vec ! [ output_lt] ,
162- ty:: Adt ( _, substs) => substs. regions ( ) . collect ( ) ,
163- _ => vec ! [ ] ,
164- } ;
169+ match * ty. skip_binder ( ) . kind ( ) {
170+ ty:: Ref ( lt, ty, Mutability :: Not ) => {
171+ match lt. kind ( ) {
172+ RegionKind :: ReLateBound ( index, region)
173+ if index. as_u32 ( ) == 0 && output_regions. contains ( & region) =>
174+ {
175+ continue ;
176+ } ,
177+ // Early bound regions on functions are either from the containing item, are bounded by another
178+ // lifetime, or are used as a bound for a type or lifetime.
179+ RegionKind :: ReEarlyBound ( ..) => continue ,
180+ _ => ( ) ,
181+ }
165182
166- if_chain ! {
167- if !output_lts. contains( input_lt) ;
168- if is_copy( cx, * ty) ;
169- if let Some ( size) = cx. layout_of( * ty) . ok( ) . map( |l| l. size. bytes( ) ) ;
170- if size <= self . ref_min_size;
171- if let hir:: TyKind :: Rptr ( _, MutTy { ty: decl_ty, .. } ) = input. kind;
172- then {
173- let value_type = if fn_body. and_then( |body| body. params. get( index) ) . map_or( false , is_self) {
174- "self" . into( )
175- } else {
176- snippet( cx, decl_ty. span, "_" ) . into( )
177- } ;
178- span_lint_and_sugg(
179- cx,
180- TRIVIALLY_COPY_PASS_BY_REF ,
181- input. span,
182- & format!( "this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)" , size, self . ref_min_size) ,
183- "consider passing by value instead" ,
184- value_type,
185- Applicability :: Unspecified ,
186- ) ;
183+ let ty = cx. tcx . erase_late_bound_regions ( fn_sig. rebind ( ty) ) ;
184+ if is_copy ( cx, ty)
185+ && let Some ( size) = cx. layout_of ( ty) . ok ( ) . map ( |l| l. size . bytes ( ) )
186+ && size <= self . ref_min_size
187+ && let hir:: TyKind :: Rptr ( _, MutTy { ty : decl_ty, .. } ) = input. kind
188+ {
189+ if let Some ( typeck) = cx. maybe_typeck_results ( ) {
190+ // Don't lint if an unsafe pointer is created.
191+ // TODO: Limit the check only to unsafe pointers to the argument (or part of the argument)
192+ // which escape the current function.
193+ if typeck. node_types ( ) . iter ( ) . any ( |( _, & ty) | ty. is_unsafe_ptr ( ) )
194+ || typeck
195+ . adjustments ( )
196+ . iter ( )
197+ . flat_map ( |( _, a) | a)
198+ . any ( |a| matches ! ( a. kind, Adjust :: Pointer ( PointerCast :: UnsafeFnPointer ) ) )
199+ {
200+ continue ;
201+ }
187202 }
203+ let value_type = if fn_body. and_then ( |body| body. params . get ( index) ) . map_or ( false , is_self) {
204+ "self" . into ( )
205+ } else {
206+ snippet ( cx, decl_ty. span , "_" ) . into ( )
207+ } ;
208+ span_lint_and_sugg (
209+ cx,
210+ TRIVIALLY_COPY_PASS_BY_REF ,
211+ input. span ,
212+ & format ! ( "this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)" , size, self . ref_min_size) ,
213+ "consider passing by value instead" ,
214+ value_type,
215+ Applicability :: Unspecified ,
216+ ) ;
188217 }
189218 } ,
190219
@@ -196,6 +225,7 @@ impl<'tcx> PassByRefOrValue {
196225 _ => continue ,
197226 }
198227 }
228+ let ty = cx. tcx . erase_late_bound_regions ( ty) ;
199229
200230 if_chain ! {
201231 if is_copy( cx, ty) ;
0 commit comments