1
1
//! Some lints that are only useful in the compiler or crates that use compiler internals, such as
2
2
//! Clippy.
3
3
4
- use rustc_hir:: HirId ;
5
4
use rustc_hir:: def:: Res ;
6
5
use 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
+ } ;
8
10
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
9
11
use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
10
12
use rustc_span:: { Span , sym} ;
@@ -101,10 +103,11 @@ declare_tool_lint! {
101
103
102
104
declare_lint_pass ! ( QueryStability => [ POTENTIAL_QUERY_INSTABILITY , UNTRACKED_QUERY_INFORMATION ] ) ;
103
105
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)
108
111
{
109
112
let def_id = instance. def_id ( ) ;
110
113
if cx. tcx . has_attr ( def_id, sym:: rustc_lint_query_instability) {
@@ -122,7 +125,119 @@ impl LateLintPass<'_> for QueryStability {
122
125
) ;
123
126
}
124
127
}
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
+ }
125
239
}
240
+ ( trait_predicates, projection_predicates)
126
241
}
127
242
128
243
declare_tool_lint ! {
0 commit comments