4
4
use rustc_hir:: HirId ;
5
5
use rustc_hir:: def:: Res ;
6
6
use rustc_hir:: def_id:: DefId ;
7
- use rustc_middle:: ty:: { self , GenericArgsRef , Ty as MiddleTy } ;
7
+ use rustc_hir:: { Expr , ExprKind } ;
8
+ use rustc_middle:: ty:: {
9
+ self , ClauseKind , GenericArgsRef , ParamTy , ProjectionPredicate , TraitPredicate , Ty as MiddleTy ,
10
+ } ;
8
11
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
9
12
use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
10
13
use rustc_span:: { Span , sym} ;
@@ -101,10 +104,11 @@ declare_tool_lint! {
101
104
102
105
declare_lint_pass ! ( QueryStability => [ POTENTIAL_QUERY_INSTABILITY , UNTRACKED_QUERY_INFORMATION ] ) ;
103
106
104
- impl LateLintPass < ' _ > for QueryStability {
105
- fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) {
106
- let Some ( ( span, def_id, args) ) = typeck_results_of_method_fn ( cx, expr) else { return } ;
107
- if let Ok ( Some ( instance) ) = ty:: Instance :: try_resolve ( cx. tcx , cx. typing_env ( ) , def_id, args)
107
+ impl < ' tcx > LateLintPass < ' tcx > for QueryStability {
108
+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
109
+ if let Some ( ( span, def_id, args) ) = typeck_results_of_method_fn ( cx, expr)
110
+ && let Ok ( Some ( instance) ) =
111
+ ty:: Instance :: try_resolve ( cx. tcx , cx. typing_env ( ) , def_id, args)
108
112
{
109
113
let def_id = instance. def_id ( ) ;
110
114
if cx. tcx . has_attr ( def_id, sym:: rustc_lint_query_instability) {
@@ -122,7 +126,119 @@ impl LateLintPass<'_> for QueryStability {
122
126
) ;
123
127
}
124
128
}
129
+ check_into_iter_stability ( cx, expr) ;
130
+ }
131
+ }
132
+
133
+ fn check_into_iter_stability < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
134
+ let Some ( into_iterator_def_id) = cx. tcx . get_diagnostic_item ( sym:: IntoIterator ) else {
135
+ return ;
136
+ } ;
137
+ // Is `expr` a function or method call?
138
+ let Some ( ( callee_def_id, generic_args, recv, args) ) =
139
+ get_callee_generic_args_and_args ( cx, expr)
140
+ else {
141
+ return ;
142
+ } ;
143
+ let fn_sig = cx. tcx . fn_sig ( callee_def_id) . instantiate_identity ( ) . skip_binder ( ) ;
144
+ let ( _, inputs) = fn_sig. inputs_and_output . as_slice ( ) . split_last ( ) . unwrap ( ) ;
145
+ for ( arg_index, & input) in inputs. iter ( ) . enumerate ( ) {
146
+ let & ty:: Param ( ParamTy { index : param_index, .. } ) = input. kind ( ) else {
147
+ continue ;
148
+ } ;
149
+ let ( trait_predicates, _) = get_input_traits_and_projections ( cx, callee_def_id, input) ;
150
+ for TraitPredicate { trait_ref, .. } in trait_predicates {
151
+ // Does the function or method require any of its arguments to implement `IntoIterator`?
152
+ if trait_ref. def_id != into_iterator_def_id {
153
+ continue ;
154
+ }
155
+ let self_ty = generic_args[ param_index as usize ] . expect_ty ( ) ;
156
+ let Some ( self_ty_adt_def) = self_ty. peel_refs ( ) . ty_adt_def ( ) else {
157
+ return ;
158
+ } ;
159
+ cx. tcx . for_each_relevant_impl ( into_iterator_def_id, self_ty, |impl_id| {
160
+ let impl_ty = cx. tcx . type_of ( impl_id) . skip_binder ( ) ;
161
+ let Some ( impl_ty_adt_def) = impl_ty. peel_refs ( ) . ty_adt_def ( ) else {
162
+ return ;
163
+ } ;
164
+ // To reduce false positives, verify that `self_ty` and `impl_ty` refer to the same ADT.
165
+ if self_ty_adt_def != impl_ty_adt_def {
166
+ return ;
167
+ }
168
+ let Some ( into_iter_item) = cx
169
+ . tcx
170
+ . associated_items ( impl_id)
171
+ . filter_by_name_unhygienic ( sym:: into_iter)
172
+ . next ( )
173
+ else {
174
+ return ;
175
+ } ;
176
+ // Does the input type's `IntoIterator` implementation have the
177
+ // `rustc_lint_query_instability` attribute on its `into_iter` method?
178
+ if !cx. tcx . has_attr ( into_iter_item. def_id , sym:: rustc_lint_query_instability) {
179
+ return ;
180
+ }
181
+ let span = if let Some ( recv) = recv {
182
+ if arg_index == 0 { recv. span } else { args[ arg_index - 1 ] . span }
183
+ } else {
184
+ args[ arg_index] . span
185
+ } ;
186
+ cx. emit_span_lint (
187
+ POTENTIAL_QUERY_INSTABILITY ,
188
+ span,
189
+ QueryInstability { query : cx. tcx . item_name ( into_iter_item. def_id ) } ,
190
+ ) ;
191
+ } ) ;
192
+ }
193
+ }
194
+ }
195
+
196
+ /// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
197
+ /// `GenericArgs`, and arguments.
198
+ fn get_callee_generic_args_and_args < ' tcx > (
199
+ cx : & LateContext < ' tcx > ,
200
+ expr : & ' tcx Expr < ' tcx > ,
201
+ ) -> Option < ( DefId , GenericArgsRef < ' tcx > , Option < & ' tcx Expr < ' tcx > > , & ' tcx [ Expr < ' tcx > ] ) > {
202
+ if let ExprKind :: Call ( callee, args) = expr. kind
203
+ && let callee_ty = cx. typeck_results ( ) . expr_ty ( callee)
204
+ && let ty:: FnDef ( callee_def_id, _) = callee_ty. kind ( )
205
+ {
206
+ let generic_args = cx. typeck_results ( ) . node_args ( callee. hir_id ) ;
207
+ return Some ( ( * callee_def_id, generic_args, None , args) ) ;
208
+ }
209
+ if let ExprKind :: MethodCall ( _, recv, args, _) = expr. kind
210
+ && let Some ( method_def_id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id )
211
+ {
212
+ let generic_args = cx. typeck_results ( ) . node_args ( expr. hir_id ) ;
213
+ return Some ( ( method_def_id, generic_args, Some ( recv) , args) ) ;
214
+ }
215
+ None
216
+ }
217
+
218
+ /// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type.
219
+ fn get_input_traits_and_projections < ' tcx > (
220
+ cx : & LateContext < ' tcx > ,
221
+ callee_def_id : DefId ,
222
+ input : MiddleTy < ' tcx > ,
223
+ ) -> ( Vec < TraitPredicate < ' tcx > > , Vec < ProjectionPredicate < ' tcx > > ) {
224
+ let mut trait_predicates = Vec :: new ( ) ;
225
+ let mut projection_predicates = Vec :: new ( ) ;
226
+ for predicate in cx. tcx . param_env ( callee_def_id) . caller_bounds ( ) {
227
+ match predicate. kind ( ) . skip_binder ( ) {
228
+ ClauseKind :: Trait ( trait_predicate) => {
229
+ if trait_predicate. trait_ref . self_ty ( ) == input {
230
+ trait_predicates. push ( trait_predicate) ;
231
+ }
232
+ }
233
+ ClauseKind :: Projection ( projection_predicate) => {
234
+ if projection_predicate. projection_term . self_ty ( ) == input {
235
+ projection_predicates. push ( projection_predicate) ;
236
+ }
237
+ }
238
+ _ => { }
239
+ }
125
240
}
241
+ ( trait_predicates, projection_predicates)
126
242
}
127
243
128
244
declare_tool_lint ! {
0 commit comments