11use clippy_config:: Conf ;
22use clippy_utils:: diagnostics:: span_lint_and_then;
3- use clippy_utils:: is_in_test;
43use clippy_utils:: msrvs:: Msrv ;
5- use rustc_attr_data_structures:: { RustcVersion , Stability , StableSince } ;
4+ use clippy_utils:: { is_in_const_context, is_in_test} ;
5+ use rustc_attr_data_structures:: { RustcVersion , StabilityLevel , StableSince } ;
66use rustc_data_structures:: fx:: FxHashMap ;
7+ use rustc_hir:: def:: DefKind ;
78use rustc_hir:: { self as hir, AmbigArg , Expr , ExprKind , HirId , QPath } ;
89use rustc_lint:: { LateContext , LateLintPass } ;
910use rustc_middle:: ty:: TyCtxt ;
@@ -80,7 +81,7 @@ enum Availability {
8081
8182pub struct IncompatibleMsrv {
8283 msrv : Msrv ,
83- availability_cache : FxHashMap < DefId , Availability > ,
84+ availability_cache : FxHashMap < ( DefId , bool ) , Availability > ,
8485 check_in_tests : bool ,
8586}
8687
@@ -96,29 +97,44 @@ impl IncompatibleMsrv {
9697 }
9798
9899 /// Returns the availability of `def_id`, whether it is enabled through a feature or
99- /// available since a given version (the default being Rust 1.0.0).
100- fn get_def_id_availability ( & mut self , tcx : TyCtxt < ' _ > , def_id : DefId ) -> Availability {
101- if let Some ( availability) = self . availability_cache . get ( & def_id) {
100+ /// available since a given version (the default being Rust 1.0.0). `needs_const` requires
101+ /// the `const`-stability to be looked up instead of the stability in non-`const` contexts.
102+ fn get_def_id_availability ( & mut self , tcx : TyCtxt < ' _ > , def_id : DefId , needs_const : bool ) -> Availability {
103+ if let Some ( availability) = self . availability_cache . get ( & ( def_id, needs_const) ) {
102104 return * availability;
103105 }
104- let stability = tcx. lookup_stability ( def_id) ;
105- let version = if stability. is_some_and ( |stability| tcx. features ( ) . enabled ( stability. feature ) ) {
106+ let ( feature, stability_level) = if needs_const {
107+ tcx. lookup_const_stability ( def_id)
108+ . map ( |stability| ( stability. feature , stability. level ) )
109+ . unzip ( )
110+ } else {
111+ tcx. lookup_stability ( def_id)
112+ . map ( |stability| ( stability. feature , stability. level ) )
113+ . unzip ( )
114+ } ;
115+ let version = if feature. is_some_and ( |feature| tcx. features ( ) . enabled ( feature) ) {
106116 Availability :: FeatureEnabled
107- } else if let Some ( StableSince :: Version ( version) ) = stability. as_ref ( ) . and_then ( Stability :: stable_since) {
117+ } else if let Some ( StableSince :: Version ( version) ) =
118+ stability_level. as_ref ( ) . and_then ( StabilityLevel :: stable_since)
119+ {
108120 Availability :: Since ( version)
121+ } else if needs_const {
122+ // Fallback to regular stability
123+ self . get_def_id_availability ( tcx, def_id, false )
109124 } else if let Some ( parent_def_id) = tcx. opt_parent ( def_id) {
110- self . get_def_id_availability ( tcx, parent_def_id)
125+ self . get_def_id_availability ( tcx, parent_def_id, needs_const )
111126 } else {
112127 Availability :: Since ( RustcVersion {
113128 major : 1 ,
114129 minor : 0 ,
115130 patch : 0 ,
116131 } )
117132 } ;
118- self . availability_cache . insert ( def_id, version) ;
133+ self . availability_cache . insert ( ( def_id, needs_const ) , version) ;
119134 version
120135 }
121136
137+ /// Emit lint if `def_id`, associated with `node` and `span`, is below the current MSRV.
122138 fn emit_lint_if_under_msrv ( & mut self , cx : & LateContext < ' _ > , def_id : DefId , node : HirId , span : Span ) {
123139 if def_id. is_local ( ) {
124140 // We don't check local items since their MSRV is supposed to always be valid.
@@ -144,17 +160,22 @@ impl IncompatibleMsrv {
144160 return ;
145161 }
146162
163+ let needs_const = cx. enclosing_body . is_some ( )
164+ && is_in_const_context ( cx)
165+ && matches ! ( cx. tcx. def_kind( def_id) , DefKind :: AssocFn | DefKind :: Fn ) ;
166+
147167 if ( self . check_in_tests || !is_in_test ( cx. tcx , node) )
148168 && let Some ( current) = self . msrv . current ( cx)
149- && let Availability :: Since ( version) = self . get_def_id_availability ( cx. tcx , def_id)
169+ && let Availability :: Since ( version) = self . get_def_id_availability ( cx. tcx , def_id, needs_const )
150170 && version > current
151171 {
152172 span_lint_and_then (
153173 cx,
154174 INCOMPATIBLE_MSRV ,
155175 span,
156176 format ! (
157- "current MSRV (Minimum Supported Rust Version) is `{current}` but this item is stable since `{version}`"
177+ "current MSRV (Minimum Supported Rust Version) is `{current}` but this item is stable{} since `{version}`" ,
178+ if needs_const { " in a `const` context" } else { "" } ,
158179 ) ,
159180 |diag| {
160181 if is_under_cfg_attribute ( cx, node) {
@@ -168,7 +189,6 @@ impl IncompatibleMsrv {
168189
169190impl < ' tcx > LateLintPass < ' tcx > for IncompatibleMsrv {
170191 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
171- // TODO: check for const stability when in const context
172192 match expr. kind {
173193 ExprKind :: MethodCall ( _, _, _, span) => {
174194 if let Some ( method_did) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id ) {
0 commit comments