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