1
1
use clippy_config:: Conf ;
2
2
use clippy_utils:: diagnostics:: span_lint_and_then;
3
- use clippy_utils:: is_in_test;
4
3
use 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 } ;
6
6
use rustc_data_structures:: fx:: FxHashMap ;
7
+ use rustc_hir:: def:: DefKind ;
7
8
use rustc_hir:: { self as hir, AmbigArg , Expr , ExprKind , HirId , QPath } ;
8
9
use rustc_lint:: { LateContext , LateLintPass } ;
9
10
use rustc_middle:: ty:: TyCtxt ;
@@ -80,7 +81,7 @@ enum Availability {
80
81
81
82
pub struct IncompatibleMsrv {
82
83
msrv : Msrv ,
83
- availability_cache : FxHashMap < DefId , Availability > ,
84
+ availability_cache : FxHashMap < ( DefId , bool ) , Availability > ,
84
85
check_in_tests : bool ,
85
86
}
86
87
@@ -96,29 +97,44 @@ impl IncompatibleMsrv {
96
97
}
97
98
98
99
/// 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) ) {
102
104
return * availability;
103
105
}
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) ) {
106
116
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
+ {
108
120
Availability :: Since ( version)
121
+ } else if needs_const {
122
+ // Fallback to regular stability
123
+ self . get_def_id_availability ( tcx, def_id, false )
109
124
} 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 )
111
126
} else {
112
127
Availability :: Since ( RustcVersion {
113
128
major : 1 ,
114
129
minor : 0 ,
115
130
patch : 0 ,
116
131
} )
117
132
} ;
118
- self . availability_cache . insert ( def_id, version) ;
133
+ self . availability_cache . insert ( ( def_id, needs_const ) , version) ;
119
134
version
120
135
}
121
136
137
+ /// Emit lint if `def_id`, associated with `node` and `span`, is below the current MSRV.
122
138
fn emit_lint_if_under_msrv ( & mut self , cx : & LateContext < ' _ > , def_id : DefId , node : HirId , span : Span ) {
123
139
if def_id. is_local ( ) {
124
140
// We don't check local items since their MSRV is supposed to always be valid.
@@ -144,17 +160,22 @@ impl IncompatibleMsrv {
144
160
return ;
145
161
}
146
162
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
+
147
167
if ( self . check_in_tests || !is_in_test ( cx. tcx , node) )
148
168
&& 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 )
150
170
&& version > current
151
171
{
152
172
span_lint_and_then (
153
173
cx,
154
174
INCOMPATIBLE_MSRV ,
155
175
span,
156
176
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 { "" } ,
158
179
) ,
159
180
|diag| {
160
181
if is_under_cfg_attribute ( cx, node) {
@@ -168,7 +189,6 @@ impl IncompatibleMsrv {
168
189
169
190
impl < ' tcx > LateLintPass < ' tcx > for IncompatibleMsrv {
170
191
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
171
- // TODO: check for const stability when in const context
172
192
match expr. kind {
173
193
ExprKind :: MethodCall ( _, _, _, span) => {
174
194
if let Some ( method_did) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id ) {
0 commit comments