1- use std:: ops:: ControlFlow ;
2-
31use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_sugg, span_lint_and_then} ;
42use clippy_utils:: source:: { snippet, snippet_with_applicability, snippet_with_context} ;
53use clippy_utils:: sugg:: Sugg ;
64use 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} ;
106use if_chain:: if_chain;
117use rustc_errors:: Applicability ;
128use rustc_hir:: def:: DefKind ;
139use rustc_hir:: def_id:: DefId ;
1410use rustc_hir:: { BindingAnnotation , Expr , ExprKind , HirId , MatchSource , Node , PatKind } ;
11+ use rustc_infer:: infer:: TyCtxtInferExt ;
12+ use rustc_infer:: traits:: Obligation ;
1513use 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 } ;
1716use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
1817use rustc_span:: { sym, Span } ;
18+ use rustc_trait_selection:: traits:: query:: evaluate_obligation:: InferCtxtExt ;
1919
2020declare_clippy_lint ! {
2121 /// ### What it does
@@ -66,10 +66,7 @@ impl MethodOrFunction {
6666}
6767
6868/// 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.
7370/// ```ignore
7471/// pub fn foo<I>(i: I)
7572/// where I: IntoIterator<Item=i32> + ExactSizeIterator
@@ -83,61 +80,42 @@ impl MethodOrFunction {
8380/// ^^^^^^^^^^^^ ... here, because `[i32; 3]` is not `ExactSizeIterator`
8481/// }
8582/// ```
86- fn exclusive_into_iter_bound (
87- cx : & LateContext < ' _ > ,
83+ fn into_iter_bound < ' tcx > (
84+ cx : & LateContext < ' tcx > ,
8885 fn_did : DefId ,
8986 into_iter_did : DefId ,
87+ into_iter_receiver : Ty < ' tcx > ,
9088 param_index : u32 ,
89+ node_args : GenericArgsRef < ' tcx > ,
9190) -> 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) ;
12992 let mut into_iter_span = None ;
13093
13194 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+ }
141119 }
142120 }
143121
@@ -225,22 +203,41 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
225203 // `fn_sig` does not ICE. (see #11065)
226204 && cx. tcx . opt_def_kind ( did) . is_some_and ( DefKind :: is_fn_like) =>
227205 {
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+ ) )
229212 }
230213 ExprKind :: MethodCall ( .., args, _) => {
231214 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+ } )
233223 }
234224 _ => None ,
235225 } ;
236226
237- if let Some ( ( parent_fn_did, args, kind) ) = parent_fn
227+ if let Some ( ( parent_fn_did, args, node_args , kind) ) = parent_fn
238228 && let Some ( into_iter_did) = cx. tcx . get_diagnostic_item ( sym:: IntoIterator )
239229 && let sig = cx. tcx . fn_sig ( parent_fn_did) . skip_binder ( ) . skip_binder ( )
240230 && let Some ( arg_pos) = args. iter ( ) . position ( |x| x. hir_id == e. hir_id )
241231 && let Some ( & into_iter_param) = sig. inputs ( ) . get ( kind. param_pos ( arg_pos) )
242232 && 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+ )
244241 && self . expn_depth == 0
245242 {
246243 // Get the "innermost" `.into_iter()` call, e.g. given this expression:
0 commit comments