Skip to content

Commit 642a972

Browse files
authored
Unrolled build for #157707
Rollup merge of #157707 - clubby789:lint-rust-version, r=JonathanBrouwer Introduce `-Z lint-rust-version` Implements rust-lang/compiler-team#950 by adding the unstable flag and updating the lint machinery to accept an MSRV. Copies most of the approach from #149870 Tracking issue: #157574
2 parents 01dfd79 + 68f4e76 commit 642a972

11 files changed

Lines changed: 113 additions & 6 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4298,6 +4298,7 @@ dependencies = [
42984298
name = "rustc_lint_defs"
42994299
version = "0.0.0"
43004300
dependencies = [
4301+
"rustc_ast",
43014302
"rustc_data_structures",
43024303
"rustc_error_messages",
43034304
"rustc_hir_id",

compiler/rustc_ast/src/attr/version.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,24 @@ impl RustcVersion {
2424
}
2525
})
2626
}
27+
28+
/// Parse a [`RustcVersion`] with an optional patch version, ignoring suffixes such as `-dev` or `-nightly`.
2729
fn parse_str(value: &str) -> Option<Self> {
28-
// Ignore any suffixes such as "-dev" or "-nightly".
2930
let mut components = value.split('-').next().unwrap().splitn(3, '.');
3031
let major = components.next()?.parse().ok()?;
3132
let minor = components.next()?.parse().ok()?;
3233
let patch = components.next().unwrap_or("0").parse().ok()?;
3334
Some(RustcVersion { major, minor, patch })
3435
}
36+
37+
/// Parse a [`RustcVersion`] which is exactly `<major>.<minor>.<patch>`, with no suffix.
38+
pub fn parse_str_strict(value: &str) -> Option<Self> {
39+
let mut components = value.splitn(3, '.');
40+
let major = components.next()?.parse().ok()?;
41+
let minor = components.next()?.parse().ok()?;
42+
let patch = components.next()?.parse().ok()?;
43+
Some(RustcVersion { major, minor, patch })
44+
}
3545
}
3646

3747
static CURRENT_OVERRIDABLE: OnceLock<RustcVersion> = OnceLock::new();

compiler/rustc_errors/src/diagnostic.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::panic;
77
use std::path::PathBuf;
88
use std::thread::panicking;
99

10+
use rustc_ast::attr::version::RustcVersion;
1011
use rustc_data_structures::sync::{DynSend, DynSync};
1112
use rustc_error_messages::{DiagArgMap, DiagArgName, DiagArgValue, IntoDiagArg};
1213
use rustc_lint_defs::{Applicability, LintExpectationId};
@@ -175,6 +176,8 @@ pub struct IsLint {
175176
pub(crate) name: String,
176177
/// Indicates whether this lint should show up in cargo's future breakage report.
177178
has_future_breakage: bool,
179+
/// Indicates the minimum rust version this lint applies to
180+
rust_version: Option<RustcVersion>,
178181
}
179182

180183
#[derive(Debug, PartialEq, Eq)]
@@ -308,6 +311,11 @@ impl DiagInner {
308311
matches!(self.is_lint, Some(IsLint { has_future_breakage: true, .. }))
309312
}
310313

314+
/// Indicates the minimum rust version this lint applies to.
315+
pub(crate) fn rust_version(&self) -> Option<RustcVersion> {
316+
self.is_lint.as_ref().and_then(|is| is.rust_version)
317+
}
318+
311319
pub(crate) fn is_force_warn(&self) -> bool {
312320
match self.level {
313321
Level::ForceWarning => {
@@ -1152,8 +1160,13 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
11521160
self
11531161
} }
11541162

1155-
pub fn is_lint(&mut self, name: String, has_future_breakage: bool) -> &mut Self {
1156-
self.is_lint = Some(IsLint { name, has_future_breakage });
1163+
pub fn is_lint(
1164+
&mut self,
1165+
name: String,
1166+
has_future_breakage: bool,
1167+
rust_version: Option<RustcVersion>,
1168+
) -> &mut Self {
1169+
self.is_lint = Some(IsLint { name, has_future_breakage, rust_version });
11571170
self
11581171
}
11591172

compiler/rustc_errors/src/lib.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub use diagnostic_impls::{
4646
};
4747
pub use emitter::ColorConfig;
4848
use emitter::{DynEmitter, Emitter};
49+
use rustc_ast::attr::version::RustcVersion;
4950
use rustc_data_structures::AtomicRef;
5051
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
5152
use rustc_data_structures::stable_hash::StableHasher;
@@ -365,6 +366,9 @@ struct DiagCtxtInner {
365366
/// The file where the ICE information is stored. This allows delayed_span_bug backtraces to be
366367
/// stored along side the main panic backtrace.
367368
ice_file: Option<PathBuf>,
369+
370+
/// Controlled by `-Z lint-rust-version`; this allows avoiding emitting lints which would raise MSRV.
371+
msrv: Option<RustcVersion>,
368372
}
369373

370374
/// A key denoting where from a diagnostic was stashed.
@@ -479,6 +483,11 @@ impl DiagCtxt {
479483
self
480484
}
481485

486+
pub fn with_msrv(mut self, msrv: RustcVersion) -> Self {
487+
self.inner.get_mut().msrv = Some(msrv);
488+
self
489+
}
490+
482491
pub fn new(emitter: Box<DynEmitter>) -> Self {
483492
Self { inner: Lock::new(DiagCtxtInner::new(emitter)) }
484493
}
@@ -526,6 +535,7 @@ impl DiagCtxt {
526535
future_breakage_diagnostics,
527536
fulfilled_expectations,
528537
ice_file: _,
538+
msrv: _,
529539
} = inner.deref_mut();
530540

531541
// For the `Vec`s and `HashMap`s, we overwrite with an empty container to free the
@@ -1193,6 +1203,7 @@ impl DiagCtxtInner {
11931203
future_breakage_diagnostics: Vec::new(),
11941204
fulfilled_expectations: Default::default(),
11951205
ice_file: None,
1206+
msrv: None,
11961207
}
11971208
}
11981209

@@ -1307,6 +1318,12 @@ impl DiagCtxtInner {
13071318
}
13081319
}
13091320

1321+
if let (Some(msrv), Some(diag_msrv)) = (self.msrv, diagnostic.rust_version())
1322+
&& diag_msrv > msrv
1323+
{
1324+
return None;
1325+
}
1326+
13101327
TRACK_DIAGNOSTIC(diagnostic, &mut |mut diagnostic| {
13111328
if let Some(code) = diagnostic.code {
13121329
self.emitted_diagnostic_codes.insert(code);

compiler/rustc_lint_defs/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2024"
55

66
[dependencies]
77
# tidy-alphabetical-start
8+
rustc_ast = { path = "../rustc_ast" }
89
rustc_data_structures = { path = "../rustc_data_structures" }
910
rustc_error_messages = { path = "../rustc_error_messages" }
1011
rustc_hir_id = { path = "../rustc_hir_id" }

compiler/rustc_lint_defs/src/lib.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::borrow::Cow;
22
use std::fmt::Display;
33

4+
use rustc_ast::attr::version::RustcVersion;
45
use rustc_data_structures::fx::FxIndexSet;
56
use rustc_data_structures::stable_hash::{StableCompare, StableHash, StableHashCtxt, StableHasher};
67
use rustc_error_messages::{DiagArgValue, IntoDiagArg};
@@ -305,6 +306,9 @@ pub struct Lint {
305306

306307
/// `true` if this lint is unaffected by `-D warnings`
307308
pub ignore_deny_warnings: bool,
309+
310+
/// Used to avoid lints which would affect MSRV
311+
pub rust_version: Option<RustcVersion>,
308312
}
309313

310314
/// Extra information for a future incompatibility lint.
@@ -521,7 +525,40 @@ impl Lint {
521525
crate_level_only: false,
522526
eval_always: false,
523527
ignore_deny_warnings: false,
528+
rust_version: None,
529+
}
530+
}
531+
532+
// FIXME(const-hack): This is used so that `declare_lint` can declare an MSRV statically.
533+
// `RustcVersion::parse_str_strict` should ideally be used instead.
534+
pub const fn parse_rust_version(version: &str) -> RustcVersion {
535+
const fn parse_part(input: &mut &[u8]) -> u16 {
536+
let mut val = 0;
537+
let mut idx = 0;
538+
while idx < input.len() {
539+
let v = input[idx];
540+
match v {
541+
b'0'..=b'9' => {
542+
val = val * 10 + (v - b'0') as u16;
543+
}
544+
b'.' => {
545+
idx += 1;
546+
break;
547+
}
548+
_ => panic!("invalid character in version"),
549+
}
550+
idx += 1;
551+
}
552+
*input = input.split_at(idx).1;
553+
val
524554
}
555+
556+
let mut bytes = version.as_bytes();
557+
let major = parse_part(&mut bytes);
558+
let minor = parse_part(&mut bytes);
559+
let patch = parse_part(&mut bytes);
560+
assert!(bytes.is_empty());
561+
RustcVersion { major, minor, patch }
525562
}
526563

527564
/// Gets the lint's name, with ASCII letters converted to lowercase.
@@ -671,6 +708,7 @@ macro_rules! declare_lint {
671708
$($field:ident : $val:expr),* $(,)*
672709
}; )?
673710
$(@edition $lint_edition:ident => $edition_level:ident;)?
711+
$(@msrv = $msrv:literal;)?
674712
$($v:ident),*) => (
675713
$(#[$attr])*
676714
$vis static $NAME: &$crate::Lint = &$crate::Lint {
@@ -687,6 +725,7 @@ macro_rules! declare_lint {
687725
}),)?
688726
$(edition_lint_opts: Some(($crate::Edition::$lint_edition, $crate::$edition_level)),)?
689727
$(eval_always: $eval_always,)?
728+
$(rust_version: Some($crate::Lint::parse_rust_version($msrv)),)?
690729
..$crate::Lint::default_fields_for_macro()
691730
};
692731
);

compiler/rustc_middle/src/lint.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>(
514514
err.disable_suggestions();
515515
}
516516

517-
err.is_lint(lint.name_lower(), has_future_breakage);
517+
err.is_lint(lint.name_lower(), has_future_breakage, lint.rust_version);
518518
// Lint diagnostics that are covered by the expect level will not be emitted outside
519519
// the compiler. It is therefore not necessary to add any information for the user.
520520
// This will therefore directly call the decorate function which will in turn emit

compiler/rustc_session/src/config.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3059,6 +3059,7 @@ pub(crate) mod dep_tracking {
30593059
use std::path::PathBuf;
30603060

30613061
use rustc_abi::Align;
3062+
use rustc_ast::attr::version::RustcVersion;
30623063
use rustc_data_structures::fx::FxIndexMap;
30633064
use rustc_data_structures::stable_hash::StableHasher;
30643065
use rustc_errors::LanguageIdentifier;
@@ -3184,7 +3185,8 @@ pub(crate) mod dep_tracking {
31843185
InliningThreshold,
31853186
FunctionReturn,
31863187
Align,
3187-
CodegenRetagOptions
3188+
CodegenRetagOptions,
3189+
RustcVersion,
31883190
);
31893191

31903192
impl<T1, T2> DepTrackingHash for (T1, T2)

compiler/rustc_session/src/errors.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,11 @@ pub fn feature_warn_issue(
8282
// Decorate this as a future-incompatibility lint as in rustc_middle::lint::lint_level
8383
let lint = UNSTABLE_SYNTAX_PRE_EXPANSION;
8484
let future_incompatible = lint.future_incompatible.as_ref().unwrap();
85-
err.is_lint(lint.name_lower(), /* has_future_breakage */ false);
85+
err.is_lint(
86+
lint.name_lower(),
87+
/* has_future_breakage */ false,
88+
/* rust_version */ None,
89+
);
8690
err.warn(lint.desc);
8791
err.note(format!("for more information, see {}", future_incompatible.reason.reference()));
8892

compiler/rustc_session/src/options.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::path::PathBuf;
44
use std::str;
55

66
use rustc_abi::Align;
7+
use rustc_ast::attr::version::RustcVersion;
78
use rustc_data_structures::fx::FxIndexMap;
89
use rustc_data_structures::profiling::TimePassesFormat;
910
use rustc_data_structures::stable_hash::StableHasher;
@@ -846,6 +847,7 @@ mod desc {
846847
super::mitigation_coverage::DeniedPartialMitigationKind::KINDS;
847848
pub(crate) const parse_deny_partial_mitigations: &str =
848849
super::mitigation_coverage::DeniedPartialMitigationKind::KINDS;
850+
pub(crate) const parse_rust_version: &str = "a rust version (`xx.yy.zz`)";
849851
}
850852

851853
pub mod parse {
@@ -2054,6 +2056,18 @@ pub mod parse {
20542056
};
20552057
true
20562058
}
2059+
2060+
pub(crate) fn parse_rust_version(slot: &mut Option<RustcVersion>, v: Option<&str>) -> bool {
2061+
let Some(v) = v else {
2062+
return false;
2063+
};
2064+
if let Some(version) = RustcVersion::parse_str_strict(v) {
2065+
*slot = Some(version);
2066+
true
2067+
} else {
2068+
false
2069+
}
2070+
}
20572071
}
20582072

20592073
options! {
@@ -2454,6 +2468,8 @@ options! {
24542468
"lint LLVM IR (default: no)"),
24552469
lint_mir: bool = (false, parse_bool, [UNTRACKED],
24562470
"lint MIR before and after each transformation"),
2471+
lint_rust_version: Option<RustcVersion> = (None, parse_rust_version, [TRACKED],
2472+
"control the minimum rust version for lints"),
24572473
llvm_module_flag: Vec<(String, u32, String)> = (Vec::new(), parse_llvm_module_flag, [TRACKED],
24582474
"a list of module flags to pass to LLVM (space separated)"),
24592475
llvm_plugins: Vec<String> = (Vec::new(), parse_list, [TRACKED],

0 commit comments

Comments
 (0)