]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_utils/src/msrvs.rs
New upstream version 1.67.1+dfsg1
[rustc.git] / src / tools / clippy / clippy_utils / src / msrvs.rs
CommitLineData
487cf647
FG
1use std::sync::OnceLock;
2
3use rustc_ast::Attribute;
cdc7bbd5 4use rustc_semver::RustcVersion;
487cf647
FG
5use rustc_session::Session;
6use rustc_span::Span;
7
8use crate::attrs::get_unique_attr;
cdc7bbd5
XL
9
10macro_rules! msrv_aliases {
11 ($($major:literal,$minor:literal,$patch:literal {
12 $($name:ident),* $(,)?
13 })*) => {
14 $($(
15 pub const $name: RustcVersion = RustcVersion::new($major, $minor, $patch);
16 )*)*
17 };
18}
19
20// names may refer to stabilized feature flags or library items
21msrv_aliases! {
487cf647 22 1,65,0 { LET_ELSE }
064997fb 23 1,62,0 { BOOL_THEN_SOME }
2b03887a 24 1,58,0 { FORMAT_ARGS_CAPTURE }
f2b60f7d 25 1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
064997fb 26 1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
487cf647 27 1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
2b03887a 28 1,50,0 { BOOL_THEN, CLAMP }
487cf647 29 1,47,0 { TAU, IS_ASCII_DIGIT_CONST }
cdc7bbd5
XL
30 1,46,0 { CONST_IF_MATCH }
31 1,45,0 { STR_STRIP_PREFIX }
c295e0f8 32 1,43,0 { LOG2_10, LOG10_2 }
5e7ed085 33 1,42,0 { MATCHES_MACRO, SLICE_PATTERNS, PTR_SLICE_RAW_PARTS }
cdc7bbd5
XL
34 1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
35 1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
064997fb 36 1,38,0 { POINTER_CAST, REM_EUCLID }
cdc7bbd5
XL
37 1,37,0 { TYPE_ALIAS_ENUM_VARIANTS }
38 1,36,0 { ITERATOR_COPIED }
39 1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
40 1,34,0 { TRY_FROM }
a2a8927a
XL
41 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
42 1,28,0 { FROM_BOOL }
064997fb 43 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN }
f2b60f7d 44 1,24,0 { IS_ASCII_DIGIT }
064997fb 45 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
04454e1e 46 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
17df50a5 47 1,16,0 { STR_REPEAT }
487cf647
FG
48 1,55,0 { SEEK_REWIND }
49}
50
51fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
52 if let Ok(version) = RustcVersion::parse(msrv) {
53 return Some(version);
54 } else if let Some(sess) = sess {
55 if let Some(span) = span {
56 sess.span_err(span, format!("`{msrv}` is not a valid Rust version"));
57 }
58 }
59 None
60}
61
62/// Tracks the current MSRV from `clippy.toml`, `Cargo.toml` or set via `#[clippy::msrv]`
63#[derive(Debug, Clone, Default)]
64pub struct Msrv {
65 stack: Vec<RustcVersion>,
66}
67
68impl Msrv {
69 fn new(initial: Option<RustcVersion>) -> Self {
70 Self {
71 stack: Vec::from_iter(initial),
72 }
73 }
74
75 fn read_inner(conf_msrv: &Option<String>, sess: &Session) -> Self {
76 let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
77 .ok()
78 .and_then(|v| parse_msrv(&v, None, None));
79 let clippy_msrv = conf_msrv.as_ref().and_then(|s| {
80 parse_msrv(s, None, None).or_else(|| {
81 sess.err(format!(
82 "error reading Clippy's configuration file. `{s}` is not a valid Rust version"
83 ));
84 None
85 })
86 });
87
88 // if both files have an msrv, let's compare them and emit a warning if they differ
89 if let Some(cargo_msrv) = cargo_msrv
90 && let Some(clippy_msrv) = clippy_msrv
91 && clippy_msrv != cargo_msrv
92 {
93 sess.warn(format!(
94 "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`"
95 ));
96 }
97
98 Self::new(clippy_msrv.or(cargo_msrv))
99 }
100
101 /// Set the initial MSRV from the Clippy config file or from Cargo due to the `rust-version`
102 /// field in `Cargo.toml`
103 ///
104 /// Returns a `&'static Msrv` as `Copy` types are more easily passed to the
105 /// `register_{late,early}_pass` callbacks
106 pub fn read(conf_msrv: &Option<String>, sess: &Session) -> &'static Self {
107 static PARSED: OnceLock<Msrv> = OnceLock::new();
108
109 PARSED.get_or_init(|| Self::read_inner(conf_msrv, sess))
110 }
111
112 pub fn current(&self) -> Option<RustcVersion> {
113 self.stack.last().copied()
114 }
115
116 pub fn meets(&self, required: RustcVersion) -> bool {
117 self.current().map_or(true, |version| version.meets(required))
118 }
119
120 fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option<RustcVersion> {
121 if let Some(msrv_attr) = get_unique_attr(sess, attrs, "msrv") {
122 if let Some(msrv) = msrv_attr.value_str() {
123 return parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
124 }
125
126 sess.span_err(msrv_attr.span, "bad clippy attribute");
127 }
128
129 None
130 }
131
132 pub fn enter_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) {
133 if let Some(version) = Self::parse_attr(sess, attrs) {
134 self.stack.push(version);
135 }
136 }
137
138 pub fn exit_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) {
139 if Self::parse_attr(sess, attrs).is_some() {
140 self.stack.pop();
141 }
142 }
cdc7bbd5 143}