44use rustc_hir:: HirId ;
55use rustc_hir:: def:: Res ;
66use 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+ } ;
811use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
912use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
1013use rustc_span:: { Span , sym} ;
@@ -101,10 +104,11 @@ declare_tool_lint! {
101104
102105declare_lint_pass ! ( QueryStability => [ POTENTIAL_QUERY_INSTABILITY , UNTRACKED_QUERY_INFORMATION ] ) ;
103106
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)
108112 {
109113 let def_id = instance. def_id ( ) ;
110114 if cx. tcx . has_attr ( def_id, sym:: rustc_lint_query_instability) {
@@ -122,7 +126,119 @@ impl LateLintPass<'_> for QueryStability {
122126 ) ;
123127 }
124128 }
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+ }
125240 }
241+ ( trait_predicates, projection_predicates)
126242}
127243
128244declare_tool_lint ! {
0 commit comments