incompatible_msrv: Don't check the contents of any std macro.

This commit is contained in:
Jason Newcomb 2025-11-12 10:56:45 -05:00
parent ee4390f070
commit a537e86e7f
3 changed files with 54 additions and 21 deletions

View file

@ -1,14 +1,14 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::Msrv;
use clippy_utils::{is_in_const_context, is_in_test};
use clippy_utils::{is_in_const_context, is_in_test, sym};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, RustcVersion, StabilityLevel, StableSince};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::impl_lint_pass;
use rustc_span::def_id::{CrateNum, DefId};
use rustc_span::{ExpnKind, Span, sym};
use rustc_span::{ExpnKind, Span};
declare_clippy_lint! {
/// ### What it does
@ -77,11 +77,36 @@ enum Availability {
Since(RustcVersion),
}
/// All known std crates containing a stability attribute.
struct StdCrates([Option<CrateNum>; 6]);
impl StdCrates {
fn new(tcx: TyCtxt<'_>) -> Self {
let mut res = Self([None; _]);
for &krate in tcx.crates(()) {
// FIXME(@Jarcho): We should have an internal lint to detect when this list is out of date.
match tcx.crate_name(krate) {
sym::alloc => res.0[0] = Some(krate),
sym::core => res.0[1] = Some(krate),
sym::core_arch => res.0[2] = Some(krate),
sym::proc_macro => res.0[3] = Some(krate),
sym::std => res.0[4] = Some(krate),
sym::std_detect => res.0[5] = Some(krate),
_ => {},
}
}
res
}
fn contains(&self, krate: CrateNum) -> bool {
self.0.contains(&Some(krate))
}
}
pub struct IncompatibleMsrv {
msrv: Msrv,
availability_cache: FxHashMap<(DefId, bool), Availability>,
check_in_tests: bool,
core_crate: Option<CrateNum>,
std_crates: StdCrates,
// The most recently called path. Used to skip checking the path after it's
// been checked when visiting the call expression.
@ -96,11 +121,7 @@ impl IncompatibleMsrv {
msrv: conf.msrv,
availability_cache: FxHashMap::default(),
check_in_tests: conf.check_incompatible_msrv_in_tests,
core_crate: tcx
.crates(())
.iter()
.find(|krate| tcx.crate_name(**krate) == sym::core)
.copied(),
std_crates: StdCrates::new(tcx),
called_path: None,
}
}
@ -152,21 +173,24 @@ impl IncompatibleMsrv {
node: HirId,
span: Span,
) {
if def_id.is_local() {
// We don't check local items since their MSRV is supposed to always be valid.
if !self.std_crates.contains(def_id.krate) {
// No stability attributes to lookup for these items.
return;
}
let expn_data = span.ctxt().outer_expn_data();
if let ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) = expn_data.kind {
// Desugared expressions get to cheat and stability is ignored.
// Intentionally not using `.from_expansion()`, since we do still care about macro expansions
return;
}
// Functions coming from `core` while expanding a macro such as `assert*!()` get to cheat too: the
// macros may have existed prior to the checked MSRV, but their expansion with a recent compiler
// might use recent functions or methods. Compiling with an older compiler would not use those.
if Some(def_id.krate) == self.core_crate && expn_data.macro_def_id.map(|did| did.krate) == self.core_crate {
return;
// Use `from_expansion` to fast-path the common case.
if span.from_expansion() {
let expn = span.ctxt().outer_expn_data();
match expn.kind {
// FIXME(@Jarcho): Check that the actual desugaring or std macro is supported by the
// current MSRV. Note that nested expansions need to be handled as well.
ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => return,
ExpnKind::Macro(..) if expn.macro_def_id.is_some_and(|did| self.std_crates.contains(did.krate)) => {
return;
},
// All other expansions share the target's MSRV.
// FIXME(@Jarcho): What should we do about version dependant macros from external crates?
_ => {},
}
}
if (self.check_in_tests || !is_in_test(cx.tcx, node))

View file

@ -126,6 +126,7 @@ generate! {
copy_from_nonoverlapping,
copy_to,
copy_to_nonoverlapping,
core_arch,
count_ones,
create,
create_new,
@ -331,6 +332,7 @@ generate! {
splitn_mut,
sqrt,
starts_with,
std_detect,
step_by,
strlen,
style,

View file

@ -178,4 +178,11 @@ const fn uncalled_len() {
//~^ incompatible_msrv
}
#[clippy::msrv = "1.0.0"]
fn vec_macro() {
let _: Vec<u32> = vec![];
let _: Vec<u32> = vec![1; 3];
let _: Vec<u32> = vec![1, 2];
}
fn main() {}