@@ -3,17 +3,20 @@ use std::iter;
3
3
4
4
use clippy_utils:: diagnostics:: span_lint_and_sugg;
5
5
use 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} ;
7
7
use clippy_utils:: { is_self, is_self_ty} ;
8
+ use core:: ops:: ControlFlow ;
8
9
use if_chain:: if_chain;
9
10
use rustc_ast:: attr;
11
+ use rustc_data_structures:: fx:: FxHashSet ;
10
12
use rustc_errors:: Applicability ;
11
13
use rustc_hir as hir;
12
14
use rustc_hir:: intravisit:: FnKind ;
13
15
use rustc_hir:: { BindingAnnotation , Body , FnDecl , HirId , Impl , ItemKind , MutTy , Mutability , Node , PatKind } ;
14
16
use rustc_lint:: { LateContext , LateLintPass } ;
15
- use rustc_middle:: ty;
17
+ use rustc_middle:: ty:: adjustment :: { Adjust , PointerCast } ;
16
18
use rustc_middle:: ty:: layout:: LayoutOf ;
19
+ use rustc_middle:: ty:: { self , RegionKind } ;
17
20
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
18
21
use rustc_span:: def_id:: LocalDefId ;
19
22
use rustc_span:: { sym, Span } ;
@@ -141,50 +144,76 @@ impl<'tcx> PassByRefOrValue {
141
144
}
142
145
143
146
let fn_sig = cx. tcx . fn_sig ( def_id) ;
144
- let fn_sig = cx. tcx . erase_late_bound_regions ( fn_sig) ;
145
-
146
147
let fn_body = cx. enclosing_body . map ( |id| cx. tcx . hir ( ) . body ( id) ) ;
147
148
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
+ {
149
163
// All spans generated from a proc-macro invocation are the same...
150
164
match span {
151
- Some ( s) if s == input. span => return ,
165
+ Some ( s) if s == input. span => continue ,
152
166
_ => ( ) ,
153
167
}
154
168
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
+ }
165
182
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
+ }
187
202
}
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
+ ) ;
188
217
}
189
218
} ,
190
219
@@ -196,6 +225,7 @@ impl<'tcx> PassByRefOrValue {
196
225
_ => continue ,
197
226
}
198
227
}
228
+ let ty = cx. tcx . erase_late_bound_regions ( ty) ;
199
229
200
230
if_chain ! {
201
231
if is_copy( cx, ty) ;
0 commit comments