@@ -3,17 +3,19 @@ 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;
1617use rustc_middle:: ty:: layout:: LayoutOf ;
18+ use rustc_middle:: ty:: { self , RegionKind } ;
1719use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
1820use rustc_span:: def_id:: LocalDefId ;
1921use rustc_span:: { sym, Span } ;
@@ -141,50 +143,62 @@ impl<'tcx> PassByRefOrValue {
141143 }
142144
143145 let fn_sig = cx. tcx . fn_sig ( def_id) ;
144- let fn_sig = cx. tcx . erase_late_bound_regions ( fn_sig) ;
145-
146146 let fn_body = cx. enclosing_body . map ( |id| cx. tcx . hir ( ) . body ( id) ) ;
147147
148- for ( index, ( input, & ty) ) in iter:: zip ( decl. inputs , fn_sig. inputs ( ) ) . enumerate ( ) {
148+ // Gather all the lifetimes found in the output type which may affect whether
149+ // `TRIVIALLY_COPY_PASS_BY_REF` should be linted.
150+ let mut output_regions = FxHashSet :: default ( ) ;
151+ for_each_top_level_late_bound_region ( fn_sig. skip_binder ( ) . output ( ) , |region| -> ControlFlow < !> {
152+ output_regions. insert ( region) ;
153+ ControlFlow :: Continue ( ( ) )
154+ } ) ;
155+
156+ for ( index, ( input, ty) ) in iter:: zip (
157+ decl. inputs ,
158+ fn_sig. skip_binder ( ) . inputs ( ) . iter ( ) . map ( |& ty| fn_sig. rebind ( ty) ) ,
159+ )
160+ . enumerate ( )
161+ {
149162 // All spans generated from a proc-macro invocation are the same...
150163 match span {
151- Some ( s) if s == input. span => return ,
164+ Some ( s) if s == input. span => continue ,
152165 _ => ( ) ,
153166 }
154167
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- } ;
168+ match * ty. skip_binder ( ) . kind ( ) {
169+ ty:: Ref ( lt, ty, Mutability :: Not ) => {
170+ match lt. kind ( ) {
171+ RegionKind :: ReLateBound ( index, region)
172+ if index. as_u32 ( ) == 0 && output_regions. contains ( & region) =>
173+ {
174+ continue ;
175+ } ,
176+ // Early bound regions on functions are either from the containing item, are bounded by another
177+ // lifetime, or are used as a bound for a type or lifetime.
178+ RegionKind :: ReEarlyBound ( ..) => continue ,
179+ _ => ( ) ,
180+ }
165181
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- ) ;
187- }
182+ let ty = cx. tcx . erase_late_bound_regions ( fn_sig. rebind ( ty) ) ;
183+ if is_copy ( cx, ty)
184+ && let Some ( size) = cx. layout_of ( ty) . ok ( ) . map ( |l| l. size . bytes ( ) )
185+ && size <= self . ref_min_size
186+ && let hir:: TyKind :: Rptr ( _, MutTy { ty : decl_ty, .. } ) = input. kind
187+ {
188+ let value_type = if fn_body. and_then ( |body| body. params . get ( index) ) . map_or ( false , is_self) {
189+ "self" . into ( )
190+ } else {
191+ snippet ( cx, decl_ty. span , "_" ) . into ( )
192+ } ;
193+ span_lint_and_sugg (
194+ cx,
195+ TRIVIALLY_COPY_PASS_BY_REF ,
196+ input. span ,
197+ & format ! ( "this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)" , size, self . ref_min_size) ,
198+ "consider passing by value instead" ,
199+ value_type,
200+ Applicability :: Unspecified ,
201+ ) ;
188202 }
189203 } ,
190204
@@ -196,6 +210,7 @@ impl<'tcx> PassByRefOrValue {
196210 _ => continue ,
197211 }
198212 }
213+ let ty = cx. tcx . erase_late_bound_regions ( ty) ;
199214
200215 if_chain ! {
201216 if is_copy( cx, ty) ;
0 commit comments