11use clippy_config:: Conf ;
22use clippy_utils:: diagnostics:: span_lint_and_then;
33use clippy_utils:: msrvs:: Msrv ;
4- use clippy_utils:: { is_in_const_context, is_in_test} ;
4+ use clippy_utils:: { is_in_const_context, is_in_test, sym } ;
55use rustc_data_structures:: fx:: FxHashMap ;
66use rustc_hir:: { self as hir, AmbigArg , Expr , ExprKind , HirId , RustcVersion , StabilityLevel , StableSince } ;
77use rustc_lint:: { LateContext , LateLintPass } ;
88use rustc_middle:: ty:: { self , TyCtxt } ;
99use rustc_session:: impl_lint_pass;
1010use rustc_span:: def_id:: { CrateNum , DefId } ;
11- use rustc_span:: { ExpnKind , Span , sym } ;
11+ use rustc_span:: { ExpnKind , Span } ;
1212
1313declare_clippy_lint ! {
1414 /// ### What it does
@@ -77,11 +77,36 @@ enum Availability {
7777 Since ( RustcVersion ) ,
7878}
7979
80+ /// All known std crates containing a stability attribute.
81+ struct StdCrates ( [ Option < CrateNum > ; 6 ] ) ;
82+ impl StdCrates {
83+ fn new ( tcx : TyCtxt < ' _ > ) -> Self {
84+ let mut res = Self ( [ None ; _] ) ;
85+ for & krate in tcx. crates ( ( ) ) {
86+ // FIXME(@Jarcho): We should have an internal lint to detect when this list is out of date.
87+ match tcx. crate_name ( krate) {
88+ sym:: alloc => res. 0 [ 0 ] = Some ( krate) ,
89+ sym:: core => res. 0 [ 1 ] = Some ( krate) ,
90+ sym:: core_arch => res. 0 [ 2 ] = Some ( krate) ,
91+ sym:: proc_macro => res. 0 [ 3 ] = Some ( krate) ,
92+ sym:: std => res. 0 [ 4 ] = Some ( krate) ,
93+ sym:: std_detect => res. 0 [ 5 ] = Some ( krate) ,
94+ _ => { } ,
95+ }
96+ }
97+ res
98+ }
99+
100+ fn contains ( & self , krate : CrateNum ) -> bool {
101+ self . 0 . contains ( & Some ( krate) )
102+ }
103+ }
104+
80105pub struct IncompatibleMsrv {
81106 msrv : Msrv ,
82107 availability_cache : FxHashMap < ( DefId , bool ) , Availability > ,
83108 check_in_tests : bool ,
84- core_crate : Option < CrateNum > ,
109+ std_crates : StdCrates ,
85110
86111 // The most recently called path. Used to skip checking the path after it's
87112 // been checked when visiting the call expression.
@@ -96,11 +121,7 @@ impl IncompatibleMsrv {
96121 msrv : conf. msrv ,
97122 availability_cache : FxHashMap :: default ( ) ,
98123 check_in_tests : conf. check_incompatible_msrv_in_tests ,
99- core_crate : tcx
100- . crates ( ( ) )
101- . iter ( )
102- . find ( |krate| tcx. crate_name ( * * krate) == sym:: core)
103- . copied ( ) ,
124+ std_crates : StdCrates :: new ( tcx) ,
104125 called_path : None ,
105126 }
106127 }
@@ -152,21 +173,24 @@ impl IncompatibleMsrv {
152173 node : HirId ,
153174 span : Span ,
154175 ) {
155- if def_id . is_local ( ) {
156- // We don't check local items since their MSRV is supposed to always be valid .
176+ if ! self . std_crates . contains ( def_id . krate ) {
177+ // No stability attributes to lookup for these items .
157178 return ;
158179 }
159- let expn_data = span. ctxt ( ) . outer_expn_data ( ) ;
160- if let ExpnKind :: AstPass ( _) | ExpnKind :: Desugaring ( _) = expn_data. kind {
161- // Desugared expressions get to cheat and stability is ignored.
162- // Intentionally not using `.from_expansion()`, since we do still care about macro expansions
163- return ;
164- }
165- // Functions coming from `core` while expanding a macro such as `assert*!()` get to cheat too: the
166- // macros may have existed prior to the checked MSRV, but their expansion with a recent compiler
167- // might use recent functions or methods. Compiling with an older compiler would not use those.
168- if Some ( def_id. krate ) == self . core_crate && expn_data. macro_def_id . map ( |did| did. krate ) == self . core_crate {
169- return ;
180+ // Use `from_expansion` to fast-path the common case.
181+ if span. from_expansion ( ) {
182+ let expn = span. ctxt ( ) . outer_expn_data ( ) ;
183+ match expn. kind {
184+ // FIXME(@Jarcho): Check that the actual desugaring or std macro is supported by the
185+ // current MSRV. Note that nested expansions need to be handled as well.
186+ ExpnKind :: AstPass ( _) | ExpnKind :: Desugaring ( _) => return ,
187+ ExpnKind :: Macro ( ..) if expn. macro_def_id . is_some_and ( |did| self . std_crates . contains ( did. krate ) ) => {
188+ return ;
189+ } ,
190+ // All other expansions share the target's MSRV.
191+ // FIXME(@Jarcho): What should we do about version dependant macros from external crates?
192+ _ => { } ,
193+ }
170194 }
171195
172196 if ( self . check_in_tests || !is_in_test ( cx. tcx , node) )
0 commit comments