1
- use std:: ops:: ControlFlow ;
2
-
3
1
use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_sugg, span_lint_and_then} ;
4
2
use clippy_utils:: source:: { snippet, snippet_with_applicability, snippet_with_context} ;
5
3
use clippy_utils:: sugg:: Sugg ;
6
4
use clippy_utils:: ty:: { is_copy, is_type_diagnostic_item, same_type_and_consts} ;
7
- use clippy_utils:: {
8
- get_parent_expr, is_diag_trait_item, is_trait_method, is_ty_alias, match_def_path, path_to_local, paths,
9
- } ;
5
+ use clippy_utils:: { get_parent_expr, is_trait_method, is_ty_alias, match_def_path, path_to_local, paths} ;
10
6
use if_chain:: if_chain;
11
7
use rustc_errors:: Applicability ;
12
8
use rustc_hir:: def:: DefKind ;
13
9
use rustc_hir:: def_id:: DefId ;
14
10
use rustc_hir:: { BindingAnnotation , Expr , ExprKind , HirId , MatchSource , Node , PatKind } ;
11
+ use rustc_infer:: infer:: TyCtxtInferExt ;
12
+ use rustc_infer:: traits:: Obligation ;
15
13
use rustc_lint:: { LateContext , LateLintPass } ;
16
- use rustc_middle:: ty:: { self , Ty , TyCtxt , TypeSuperVisitable , TypeVisitable , TypeVisitor } ;
14
+ use rustc_middle:: traits:: ObligationCause ;
15
+ use rustc_middle:: ty:: { self , EarlyBinder , GenericArg , GenericArgsRef , Ty } ;
17
16
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
18
17
use rustc_span:: { sym, Span } ;
18
+ use rustc_trait_selection:: traits:: query:: evaluate_obligation:: InferCtxtExt ;
19
19
20
20
declare_clippy_lint ! {
21
21
/// ### What it does
@@ -66,10 +66,7 @@ impl MethodOrFunction {
66
66
}
67
67
68
68
/// Returns the span of the `IntoIterator` trait bound in the function pointed to by `fn_did`,
69
- /// iff the `IntoIterator` bound is the only bound on the type parameter.
70
- ///
71
- /// This last part is important because it might be that the type the user is calling `.into_iter()`
72
- /// on might not satisfy those other bounds and would result in compile errors:
69
+ /// iff all of the bounds also hold for the type of the `.into_iter()` receiver.
73
70
/// ```ignore
74
71
/// pub fn foo<I>(i: I)
75
72
/// where I: IntoIterator<Item=i32> + ExactSizeIterator
@@ -83,61 +80,42 @@ impl MethodOrFunction {
83
80
/// ^^^^^^^^^^^^ ... here, because `[i32; 3]` is not `ExactSizeIterator`
84
81
/// }
85
82
/// ```
86
- fn exclusive_into_iter_bound (
87
- cx : & LateContext < ' _ > ,
83
+ fn into_iter_bound < ' tcx > (
84
+ cx : & LateContext < ' tcx > ,
88
85
fn_did : DefId ,
89
86
into_iter_did : DefId ,
87
+ into_iter_receiver : Ty < ' tcx > ,
90
88
param_index : u32 ,
89
+ node_args : GenericArgsRef < ' tcx > ,
91
90
) -> Option < Span > {
92
- #[ derive( Clone ) ]
93
- struct ExplicitlyUsedTyParam < ' a , ' tcx > {
94
- cx : & ' a LateContext < ' tcx > ,
95
- param_index : u32 ,
96
- }
97
-
98
- impl < ' a , ' tcx > TypeVisitor < TyCtxt < ' tcx > > for ExplicitlyUsedTyParam < ' a , ' tcx > {
99
- type BreakTy = ( ) ;
100
- fn visit_predicate ( & mut self , p : ty:: Predicate < ' tcx > ) -> ControlFlow < Self :: BreakTy > {
101
- // Ignore implicit `T: Sized` bound
102
- if let ty:: PredicateKind :: Clause ( ty:: ClauseKind :: Trait ( tr) ) = p. kind ( ) . skip_binder ( )
103
- && let Some ( sized_trait_did) = self . cx . tcx . lang_items ( ) . sized_trait ( )
104
- && sized_trait_did == tr. def_id ( )
105
- {
106
- return ControlFlow :: Continue ( ( ) ) ;
107
- }
108
-
109
- // Ignore `<T as IntoIterator>::Item` projection, this use of the ty param specifically is fine
110
- // because it's what we're already looking for
111
- if let ty:: PredicateKind :: Clause ( ty:: ClauseKind :: Projection ( proj) ) = p. kind ( ) . skip_binder ( )
112
- && is_diag_trait_item ( self . cx , proj. projection_ty . def_id , sym:: IntoIterator )
113
- {
114
- return ControlFlow :: Continue ( ( ) ) ;
115
- }
116
-
117
- p. super_visit_with ( self )
118
- }
119
-
120
- fn visit_ty ( & mut self , t : Ty < ' tcx > ) -> ControlFlow < Self :: BreakTy > {
121
- if t. is_param ( self . param_index ) {
122
- ControlFlow :: Break ( ( ) )
123
- } else {
124
- ControlFlow :: Continue ( ( ) )
125
- }
126
- }
127
- }
128
-
91
+ let param_env = cx. tcx . param_env ( fn_did) ;
129
92
let mut into_iter_span = None ;
130
93
131
94
for ( pred, span) in cx. tcx . explicit_predicates_of ( fn_did) . predicates {
132
- if let ty:: ClauseKind :: Trait ( tr) = pred. kind ( ) . skip_binder ( )
133
- && tr. def_id ( ) == into_iter_did
134
- && tr. self_ty ( ) . is_param ( param_index)
135
- {
136
- into_iter_span = Some ( * span) ;
137
- } else if pred. visit_with ( & mut ExplicitlyUsedTyParam { cx, param_index } ) . is_break ( ) {
138
- // Found another reference of the type parameter; conservatively assume
139
- // that we can't remove the bound.
140
- return None ;
95
+ if let ty:: ClauseKind :: Trait ( tr) = pred. kind ( ) . skip_binder ( ) {
96
+ if tr. def_id ( ) == into_iter_did && tr. self_ty ( ) . is_param ( param_index) {
97
+ into_iter_span = Some ( * span) ;
98
+ } else {
99
+ // Substitute generics in the predicate and replace the IntoIterator type parameter with the
100
+ // `.into_iter()` receiver to see if the bound also holds for that type.
101
+ let args = cx. tcx . mk_args_from_iter ( node_args. iter ( ) . enumerate ( ) . map ( |( i, arg) | {
102
+ if i == param_index as usize {
103
+ GenericArg :: from ( into_iter_receiver)
104
+ } else {
105
+ arg
106
+ }
107
+ } ) ) ;
108
+ let predicate = EarlyBinder :: bind ( tr) . instantiate ( cx. tcx , args) ;
109
+ let obligation = Obligation :: new ( cx. tcx , ObligationCause :: dummy ( ) , param_env, predicate) ;
110
+ if !cx
111
+ . tcx
112
+ . infer_ctxt ( )
113
+ . build ( )
114
+ . predicate_must_hold_modulo_regions ( & obligation)
115
+ {
116
+ return None ;
117
+ }
118
+ }
141
119
}
142
120
}
143
121
@@ -225,22 +203,41 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
225
203
// `fn_sig` does not ICE. (see #11065)
226
204
&& cx. tcx . opt_def_kind ( did) . is_some_and ( DefKind :: is_fn_like) =>
227
205
{
228
- Some ( ( did, args, MethodOrFunction :: Function ) )
206
+ Some ( (
207
+ did,
208
+ args,
209
+ cx. typeck_results ( ) . node_args ( recv. hir_id ) ,
210
+ MethodOrFunction :: Function
211
+ ) )
229
212
}
230
213
ExprKind :: MethodCall ( .., args, _) => {
231
214
cx. typeck_results ( ) . type_dependent_def_id ( parent. hir_id )
232
- . map ( |did| ( did, args, MethodOrFunction :: Method ) )
215
+ . map ( |did| {
216
+ return (
217
+ did,
218
+ args,
219
+ cx. typeck_results ( ) . node_args ( recv. hir_id ) ,
220
+ MethodOrFunction :: Method
221
+ ) ;
222
+ } )
233
223
}
234
224
_ => None ,
235
225
} ;
236
226
237
- if let Some ( ( parent_fn_did, args, kind) ) = parent_fn
227
+ if let Some ( ( parent_fn_did, args, node_args , kind) ) = parent_fn
238
228
&& let Some ( into_iter_did) = cx. tcx . get_diagnostic_item ( sym:: IntoIterator )
239
229
&& let sig = cx. tcx . fn_sig ( parent_fn_did) . skip_binder ( ) . skip_binder ( )
240
230
&& let Some ( arg_pos) = args. iter ( ) . position ( |x| x. hir_id == e. hir_id )
241
231
&& let Some ( & into_iter_param) = sig. inputs ( ) . get ( kind. param_pos ( arg_pos) )
242
232
&& let ty:: Param ( param) = into_iter_param. kind ( )
243
- && let Some ( span) = exclusive_into_iter_bound ( cx, parent_fn_did, into_iter_did, param. index )
233
+ && let Some ( span) = into_iter_bound (
234
+ cx,
235
+ parent_fn_did,
236
+ into_iter_did,
237
+ cx. typeck_results ( ) . expr_ty ( into_iter_recv) ,
238
+ param. index ,
239
+ node_args
240
+ )
244
241
&& self . expn_depth == 0
245
242
{
246
243
// Get the "innermost" `.into_iter()` call, e.g. given this expression:
0 commit comments