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