Skip to content

Commit da2a220

Browse files
authored
incompatible_msrv: Don't check the contents of any std macro. (#16083)
cc #148190 @flip1995 @RalfJung This is a bit of a hack in that it hardcodes the list of crates with stability attributes. This shouldn't be a big deal since that isn't a set that changes very frequently and an internal lint could detect when that happens. The `fixme`s added aren't new issues and shouldn't get in the way of unblocking the upstream issue. changelog: none
2 parents d599529 + a537e86 commit da2a220

File tree

3 files changed

+54
-21
lines changed

3 files changed

+54
-21
lines changed

clippy_lints/src/incompatible_msrv.rs

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
use clippy_config::Conf;
22
use clippy_utils::diagnostics::span_lint_and_then;
33
use 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};
55
use rustc_data_structures::fx::FxHashMap;
66
use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, RustcVersion, StabilityLevel, StableSince};
77
use rustc_lint::{LateContext, LateLintPass};
88
use rustc_middle::ty::{self, TyCtxt};
99
use rustc_session::impl_lint_pass;
1010
use rustc_span::def_id::{CrateNum, DefId};
11-
use rustc_span::{ExpnKind, Span, sym};
11+
use rustc_span::{ExpnKind, Span};
1212

1313
declare_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+
80105
pub 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))

clippy_utils/src/sym.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ generate! {
126126
copy_from_nonoverlapping,
127127
copy_to,
128128
copy_to_nonoverlapping,
129+
core_arch,
129130
count_ones,
130131
create,
131132
create_new,
@@ -331,6 +332,7 @@ generate! {
331332
splitn_mut,
332333
sqrt,
333334
starts_with,
335+
std_detect,
334336
step_by,
335337
strlen,
336338
style,

tests/ui/incompatible_msrv.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,11 @@ const fn uncalled_len() {
178178
//~^ incompatible_msrv
179179
}
180180

181+
#[clippy::msrv = "1.0.0"]
182+
fn vec_macro() {
183+
let _: Vec<u32> = vec![];
184+
let _: Vec<u32> = vec![1; 3];
185+
let _: Vec<u32> = vec![1, 2];
186+
}
187+
181188
fn main() {}

0 commit comments

Comments
 (0)