diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index 716334656926..c3bc9048c23a 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -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; 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, + 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)) diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 8e8a80a6a9c9..72d8f6df8d05 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -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, diff --git a/tests/ui/incompatible_msrv.rs b/tests/ui/incompatible_msrv.rs index 3069c8139abe..e08828b46c36 100644 --- a/tests/ui/incompatible_msrv.rs +++ b/tests/ui/incompatible_msrv.rs @@ -178,4 +178,11 @@ const fn uncalled_len() { //~^ incompatible_msrv } +#[clippy::msrv = "1.0.0"] +fn vec_macro() { + let _: Vec = vec![]; + let _: Vec = vec![1; 3]; + let _: Vec = vec![1, 2]; +} + fn main() {}