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:: { self , ClauseKind , GenericArgsRef , PredicatePolarity , TraitPredicate , Ty } ;
88use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
99use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
1010use rustc_span:: { Span , sym} ;
@@ -56,25 +56,6 @@ impl LateLintPass<'_> for DefaultHashTypes {
5656 }
5757}
5858
59- /// Helper function for lints that check for expressions with calls and use typeck results to
60- /// get the `DefId` and `GenericArgsRef` of the function.
61- fn typeck_results_of_method_fn < ' tcx > (
62- cx : & LateContext < ' tcx > ,
63- expr : & hir:: Expr < ' _ > ,
64- ) -> Option < ( Span , DefId , ty:: GenericArgsRef < ' tcx > ) > {
65- match expr. kind {
66- hir:: ExprKind :: MethodCall ( segment, ..)
67- if let Some ( def_id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id ) =>
68- {
69- Some ( ( segment. ident . span , def_id, cx. typeck_results ( ) . node_args ( expr. hir_id ) ) )
70- }
71- _ => match cx. typeck_results ( ) . node_type ( expr. hir_id ) . kind ( ) {
72- & ty:: FnDef ( def_id, args) => Some ( ( expr. span , def_id, args) ) ,
73- _ => None ,
74- } ,
75- }
76- }
77-
7859declare_tool_lint ! {
7960 /// The `potential_query_instability` lint detects use of methods which can lead to
8061 /// potential query instability, such as iterating over a `HashMap`.
@@ -101,10 +82,12 @@ declare_tool_lint! {
10182
10283declare_lint_pass ! ( QueryStability => [ POTENTIAL_QUERY_INSTABILITY , UNTRACKED_QUERY_INFORMATION ] ) ;
10384
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)
85+ impl < ' tcx > LateLintPass < ' tcx > for QueryStability {
86+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
87+ if let Some ( ( callee_def_id, span, generic_args, _recv, _args) ) =
88+ get_callee_span_generic_args_and_args ( cx, expr)
89+ && let Ok ( Some ( instance) ) =
90+ ty:: Instance :: try_resolve ( cx. tcx , cx. typing_env ( ) , callee_def_id, generic_args)
10891 {
10992 let def_id = instance. def_id ( ) ;
11093 if cx. tcx . has_attr ( def_id, sym:: rustc_lint_query_instability) {
@@ -113,7 +96,15 @@ impl LateLintPass<'_> for QueryStability {
11396 span,
11497 QueryInstability { query : cx. tcx . item_name ( def_id) } ,
11598 ) ;
99+ } else if has_unstable_into_iter_predicate ( cx, callee_def_id, generic_args) {
100+ let call_span = span. with_hi ( expr. span . hi ( ) ) ;
101+ cx. emit_span_lint (
102+ POTENTIAL_QUERY_INSTABILITY ,
103+ call_span,
104+ QueryInstability { query : sym:: into_iter } ,
105+ ) ;
116106 }
107+
117108 if cx. tcx . has_attr ( def_id, sym:: rustc_lint_untracked_query_information) {
118109 cx. emit_span_lint (
119110 UNTRACKED_QUERY_INFORMATION ,
@@ -125,6 +116,64 @@ impl LateLintPass<'_> for QueryStability {
125116 }
126117}
127118
119+ fn has_unstable_into_iter_predicate < ' tcx > (
120+ cx : & LateContext < ' tcx > ,
121+ callee_def_id : DefId ,
122+ generic_args : GenericArgsRef < ' tcx > ,
123+ ) -> bool {
124+ let Some ( into_iterator_def_id) = cx. tcx . get_diagnostic_item ( sym:: IntoIterator ) else {
125+ return false ;
126+ } ;
127+ let Some ( into_iter_fn_def_id) = cx. tcx . lang_items ( ) . into_iter_fn ( ) else {
128+ return false ;
129+ } ;
130+ let predicates = cx. tcx . predicates_of ( callee_def_id) . instantiate ( cx. tcx , generic_args) ;
131+ for ( predicate, _) in predicates {
132+ let ClauseKind :: Trait ( TraitPredicate { trait_ref, polarity : PredicatePolarity :: Positive } ) =
133+ predicate. kind ( ) . skip_binder ( )
134+ else {
135+ continue ;
136+ } ;
137+ // Does the function or method require any of its arguments to implement `IntoIterator`?
138+ if trait_ref. def_id != into_iterator_def_id {
139+ continue ;
140+ }
141+ let Ok ( Some ( instance) ) =
142+ ty:: Instance :: try_resolve ( cx. tcx , cx. typing_env ( ) , into_iter_fn_def_id, trait_ref. args )
143+ else {
144+ continue ;
145+ } ;
146+ // Does the input type's `IntoIterator` implementation have the
147+ // `rustc_lint_query_instability` attribute on its `into_iter` method?
148+ if cx. tcx . has_attr ( instance. def_id ( ) , sym:: rustc_lint_query_instability) {
149+ return true ;
150+ }
151+ }
152+ false
153+ }
154+
155+ /// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
156+ /// `Span`, `GenericArgs`, and arguments. This is a slight augmentation of a similarly named Clippy
157+ /// function, `get_callee_generic_args_and_args`.
158+ fn get_callee_span_generic_args_and_args < ' tcx > (
159+ cx : & LateContext < ' tcx > ,
160+ expr : & ' tcx Expr < ' tcx > ,
161+ ) -> Option < ( DefId , Span , GenericArgsRef < ' tcx > , Option < & ' tcx Expr < ' tcx > > , & ' tcx [ Expr < ' tcx > ] ) > {
162+ if let ExprKind :: Call ( callee, args) = expr. kind
163+ && let callee_ty = cx. typeck_results ( ) . expr_ty ( callee)
164+ && let ty:: FnDef ( callee_def_id, generic_args) = callee_ty. kind ( )
165+ {
166+ return Some ( ( * callee_def_id, callee. span , generic_args, None , args) ) ;
167+ }
168+ if let ExprKind :: MethodCall ( segment, recv, args, _) = expr. kind
169+ && let Some ( method_def_id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id )
170+ {
171+ let generic_args = cx. typeck_results ( ) . node_args ( expr. hir_id ) ;
172+ return Some ( ( method_def_id, segment. ident . span , generic_args, Some ( recv) , args) ) ;
173+ }
174+ None
175+ }
176+
128177declare_tool_lint ! {
129178 /// The `usage_of_ty_tykind` lint detects usages of `ty::TyKind::<kind>`,
130179 /// where `ty::<kind>` would suffice.
@@ -461,33 +510,22 @@ declare_tool_lint! {
461510declare_lint_pass ! ( Diagnostics => [ UNTRANSLATABLE_DIAGNOSTIC , DIAGNOSTIC_OUTSIDE_OF_IMPL ] ) ;
462511
463512impl LateLintPass < ' _ > for Diagnostics {
464- fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) {
513+ fn check_expr < ' tcx > ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' tcx > ) {
465514 let collect_args_tys_and_spans = |args : & [ hir:: Expr < ' _ > ] , reserve_one_extra : bool | {
466515 let mut result = Vec :: with_capacity ( args. len ( ) + usize:: from ( reserve_one_extra) ) ;
467516 result. extend ( args. iter ( ) . map ( |arg| ( cx. typeck_results ( ) . expr_ty ( arg) , arg. span ) ) ) ;
468517 result
469518 } ;
470519 // Only check function calls and method calls.
471- let ( span, def_id, fn_gen_args, arg_tys_and_spans) = match expr. kind {
472- hir:: ExprKind :: Call ( callee, args) => {
473- match cx. typeck_results ( ) . node_type ( callee. hir_id ) . kind ( ) {
474- & ty:: FnDef ( def_id, fn_gen_args) => {
475- ( callee. span , def_id, fn_gen_args, collect_args_tys_and_spans ( args, false ) )
476- }
477- _ => return , // occurs for fns passed as args
478- }
479- }
480- hir:: ExprKind :: MethodCall ( _segment, _recv, args, _span) => {
481- let Some ( ( span, def_id, fn_gen_args) ) = typeck_results_of_method_fn ( cx, expr)
482- else {
483- return ;
484- } ;
485- let mut args = collect_args_tys_and_spans ( args, true ) ;
486- args. insert ( 0 , ( cx. tcx . types . self_param , _recv. span ) ) ; // dummy inserted for `self`
487- ( span, def_id, fn_gen_args, args)
488- }
489- _ => return ,
520+ let Some ( ( def_id, span, fn_gen_args, recv, args) ) =
521+ get_callee_span_generic_args_and_args ( cx, expr)
522+ else {
523+ return ;
490524 } ;
525+ let mut arg_tys_and_spans = collect_args_tys_and_spans ( args, recv. is_some ( ) ) ;
526+ if let Some ( recv) = recv {
527+ arg_tys_and_spans. insert ( 0 , ( cx. tcx . types . self_param , recv. span ) ) ; // dummy inserted for `self`
528+ }
491529
492530 Self :: diagnostic_outside_of_impl ( cx, span, expr. hir_id , def_id, fn_gen_args) ;
493531 Self :: untranslatable_diagnostic ( cx, def_id, & arg_tys_and_spans) ;
@@ -496,7 +534,7 @@ impl LateLintPass<'_> for Diagnostics {
496534
497535impl Diagnostics {
498536 // Is the type `{D,Subd}iagMessage`?
499- fn is_diag_message < ' cx > ( cx : & LateContext < ' cx > , ty : MiddleTy < ' cx > ) -> bool {
537+ fn is_diag_message < ' cx > ( cx : & LateContext < ' cx > , ty : Ty < ' cx > ) -> bool {
500538 if let Some ( adt_def) = ty. ty_adt_def ( )
501539 && let Some ( name) = cx. tcx . get_diagnostic_name ( adt_def. did ( ) )
502540 && matches ! ( name, sym:: DiagMessage | sym:: SubdiagMessage )
@@ -510,7 +548,7 @@ impl Diagnostics {
510548 fn untranslatable_diagnostic < ' cx > (
511549 cx : & LateContext < ' cx > ,
512550 def_id : DefId ,
513- arg_tys_and_spans : & [ ( MiddleTy < ' cx > , Span ) ] ,
551+ arg_tys_and_spans : & [ ( Ty < ' cx > , Span ) ] ,
514552 ) {
515553 let fn_sig = cx. tcx . fn_sig ( def_id) . instantiate_identity ( ) . skip_binder ( ) ;
516554 let predicates = cx. tcx . predicates_of ( def_id) . instantiate_identity ( cx. tcx ) . predicates ;
0 commit comments