Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 45 additions & 21 deletions clippy_lints/src/incompatible_msrv.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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,
}
}
Expand Down Expand Up @@ -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))
Expand Down
2 changes: 2 additions & 0 deletions clippy_utils/src/sym.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ generate! {
copy_from_nonoverlapping,
copy_to,
copy_to_nonoverlapping,
core_arch,
count_ones,
create,
create_new,
Expand Down Expand Up @@ -331,6 +332,7 @@ generate! {
splitn_mut,
sqrt,
starts_with,
std_detect,
step_by,
strlen,
style,
Expand Down
7 changes: 7 additions & 0 deletions tests/ui/incompatible_msrv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}