@@ -3,10 +3,9 @@ use clippy_utils::diagnostics::span_lint_and_then;
33use clippy_utils:: msrvs:: Msrv ;
44use clippy_utils:: { is_in_const_context, is_in_test} ;
55use rustc_data_structures:: fx:: FxHashMap ;
6- use rustc_hir:: def:: DefKind ;
76use rustc_hir:: { self as hir, AmbigArg , Expr , ExprKind , HirId , QPath , RustcVersion , StabilityLevel , StableSince } ;
87use rustc_lint:: { LateContext , LateLintPass } ;
9- use rustc_middle:: ty:: TyCtxt ;
8+ use rustc_middle:: ty:: { self , TyCtxt } ;
109use rustc_session:: impl_lint_pass;
1110use rustc_span:: def_id:: { CrateNum , DefId } ;
1211use rustc_span:: { ExpnKind , Span , sym} ;
@@ -83,6 +82,10 @@ pub struct IncompatibleMsrv {
8382 availability_cache : FxHashMap < ( DefId , bool ) , Availability > ,
8483 check_in_tests : bool ,
8584 core_crate : Option < CrateNum > ,
85+
86+ // The most recently called path. Used to skip checking the path after it's
87+ // been checked when visiting the call expression.
88+ called_path : Option < HirId > ,
8689}
8790
8891impl_lint_pass ! ( IncompatibleMsrv => [ INCOMPATIBLE_MSRV ] ) ;
@@ -98,6 +101,7 @@ impl IncompatibleMsrv {
98101 . iter ( )
99102 . find ( |krate| tcx. crate_name ( * * krate) == sym:: core)
100103 . copied ( ) ,
104+ called_path : None ,
101105 }
102106 }
103107
@@ -140,7 +144,14 @@ impl IncompatibleMsrv {
140144 }
141145
142146 /// Emit lint if `def_id`, associated with `node` and `span`, is below the current MSRV.
143- fn emit_lint_if_under_msrv ( & mut self , cx : & LateContext < ' _ > , def_id : DefId , node : HirId , span : Span ) {
147+ fn emit_lint_if_under_msrv (
148+ & mut self ,
149+ cx : & LateContext < ' _ > ,
150+ needs_const : bool ,
151+ def_id : DefId ,
152+ node : HirId ,
153+ span : Span ,
154+ ) {
144155 if def_id. is_local ( ) {
145156 // We don't check local items since their MSRV is supposed to always be valid.
146157 return ;
@@ -158,10 +169,6 @@ impl IncompatibleMsrv {
158169 return ;
159170 }
160171
161- let needs_const = cx. enclosing_body . is_some ( )
162- && is_in_const_context ( cx)
163- && matches ! ( cx. tcx. def_kind( def_id) , DefKind :: AssocFn | DefKind :: Fn ) ;
164-
165172 if ( self . check_in_tests || !is_in_test ( cx. tcx , node) )
166173 && let Some ( current) = self . msrv . current ( cx)
167174 && let Availability :: Since ( version) = self . get_def_id_availability ( cx. tcx , def_id, needs_const)
@@ -190,16 +197,35 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv {
190197 match expr. kind {
191198 ExprKind :: MethodCall ( _, _, _, span) => {
192199 if let Some ( method_did) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id ) {
193- self . emit_lint_if_under_msrv ( cx, method_did, expr. hir_id , span) ;
200+ self . emit_lint_if_under_msrv ( cx, is_in_const_context ( cx ) , method_did, expr. hir_id , span) ;
194201 }
195202 } ,
203+ ExprKind :: Call ( callee, _)
204+ if let ExprKind :: Path ( qpath @ ( QPath :: Resolved ( ..) | QPath :: TypeRelative ( ..) ) ) = callee. kind =>
205+ {
206+ self . called_path = Some ( callee. hir_id ) ;
207+ let needs_const = is_in_const_context ( cx) ;
208+ let def_id = if let Some ( def_id) = cx. qpath_res ( & qpath, callee. hir_id ) . opt_def_id ( ) {
209+ def_id
210+ } else if needs_const && let ty:: FnDef ( def_id, _) = * cx. typeck_results ( ) . expr_ty ( callee) . kind ( ) {
211+ // Edge case where a function is first assigned then called.
212+ // We previously would have warned for the non-const MSRV, when
213+ // checking the path, but now that it's called the const MSRV
214+ // must also be met.
215+ def_id
216+ } else {
217+ return ;
218+ } ;
219+ self . emit_lint_if_under_msrv ( cx, needs_const, def_id, expr. hir_id , callee. span ) ;
220+ } ,
196221 // Desugaring into function calls by the compiler will use `QPath::LangItem` variants. Those should
197222 // not be linted as they will not be generated in older compilers if the function is not available,
198223 // and the compiler is allowed to call unstable functions.
199- ExprKind :: Path ( qpath @ ( QPath :: Resolved ( ..) | QPath :: TypeRelative ( ..) ) ) => {
200- if let Some ( path_def_id) = cx. qpath_res ( & qpath, expr. hir_id ) . opt_def_id ( ) {
201- self . emit_lint_if_under_msrv ( cx, path_def_id, expr. hir_id , expr. span ) ;
202- }
224+ ExprKind :: Path ( qpath @ ( QPath :: Resolved ( ..) | QPath :: TypeRelative ( ..) ) )
225+ if let Some ( path_def_id) = cx. qpath_res ( & qpath, expr. hir_id ) . opt_def_id ( )
226+ && self . called_path != Some ( expr. hir_id ) =>
227+ {
228+ self . emit_lint_if_under_msrv ( cx, false , path_def_id, expr. hir_id , expr. span ) ;
203229 } ,
204230 _ => { } ,
205231 }
@@ -211,7 +237,7 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv {
211237 // `CStr` and `CString` have been moved around but have been available since Rust 1.0.0
212238 && !matches ! ( cx. tcx. get_diagnostic_name( ty_def_id) , Some ( sym:: cstr_type | sym:: cstring_type) )
213239 {
214- self . emit_lint_if_under_msrv ( cx, ty_def_id, hir_ty. hir_id , hir_ty. span ) ;
240+ self . emit_lint_if_under_msrv ( cx, false , ty_def_id, hir_ty. hir_id , hir_ty. span ) ;
215241 }
216242 }
217243}
0 commit comments