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
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4298,6 +4298,7 @@ dependencies = [
name = "rustc_lint_defs"
version = "0.0.0"
dependencies = [
"rustc_ast",
"rustc_data_structures",
"rustc_error_messages",
"rustc_hir_id",
Expand Down
12 changes: 11 additions & 1 deletion compiler/rustc_ast/src/attr/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,24 @@ impl RustcVersion {
}
})
}

/// Parse a [`RustcVersion`] with an optional patch version, ignoring suffixes such as `-dev` or `-nightly`.
fn parse_str(value: &str) -> Option<Self> {
// Ignore any suffixes such as "-dev" or "-nightly".
let mut components = value.split('-').next().unwrap().splitn(3, '.');
let major = components.next()?.parse().ok()?;
let minor = components.next()?.parse().ok()?;
let patch = components.next().unwrap_or("0").parse().ok()?;
Some(RustcVersion { major, minor, patch })
}

/// Parse a [`RustcVersion`] which is exactly `<major>.<minor>.<patch>`, with no suffix.
pub fn parse_str_strict(value: &str) -> Option<Self> {
let mut components = value.splitn(3, '.');
let major = components.next()?.parse().ok()?;
let minor = components.next()?.parse().ok()?;
let patch = components.next()?.parse().ok()?;
Some(RustcVersion { major, minor, patch })
}
}

static CURRENT_OVERRIDABLE: OnceLock<RustcVersion> = OnceLock::new();
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_attr_parsing/src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use rustc_ast::ast::Safety;
use rustc_hir::AttrStyle;
use rustc_span::Symbol;

/// A template that the attribute input must match.
/// A template to suggest the correct syntax of an attribute.
///
/// This is not used to *check* attributes. The attribute's parser is responsible for that.
/// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now.
#[derive(Clone, Copy, Default)]
pub struct AttributeTemplate {
Expand Down
17 changes: 15 additions & 2 deletions compiler/rustc_errors/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::panic;
use std::path::PathBuf;
use std::thread::panicking;

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

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

/// Indicates the minimum rust version this lint applies to.
pub(crate) fn rust_version(&self) -> Option<RustcVersion> {
self.is_lint.as_ref().and_then(|is| is.rust_version)
}

pub(crate) fn is_force_warn(&self) -> bool {
match self.level {
Level::ForceWarning => {
Expand Down Expand Up @@ -1152,8 +1160,13 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
self
} }

pub fn is_lint(&mut self, name: String, has_future_breakage: bool) -> &mut Self {
self.is_lint = Some(IsLint { name, has_future_breakage });
pub fn is_lint(
&mut self,
name: String,
has_future_breakage: bool,
rust_version: Option<RustcVersion>,
) -> &mut Self {
self.is_lint = Some(IsLint { name, has_future_breakage, rust_version });
self
}

Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub use diagnostic_impls::{
};
pub use emitter::ColorConfig;
use emitter::{DynEmitter, Emitter};
use rustc_ast::attr::version::RustcVersion;
use rustc_data_structures::AtomicRef;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::stable_hash::StableHasher;
Expand Down Expand Up @@ -365,6 +366,9 @@ struct DiagCtxtInner {
/// The file where the ICE information is stored. This allows delayed_span_bug backtraces to be
/// stored along side the main panic backtrace.
ice_file: Option<PathBuf>,

/// Controlled by `-Z lint-rust-version`; this allows avoiding emitting lints which would raise MSRV.
msrv: Option<RustcVersion>,
}

/// A key denoting where from a diagnostic was stashed.
Expand Down Expand Up @@ -479,6 +483,11 @@ impl DiagCtxt {
self
}

pub fn with_msrv(mut self, msrv: RustcVersion) -> Self {
self.inner.get_mut().msrv = Some(msrv);
self
}

pub fn new(emitter: Box<DynEmitter>) -> Self {
Self { inner: Lock::new(DiagCtxtInner::new(emitter)) }
}
Expand Down Expand Up @@ -526,6 +535,7 @@ impl DiagCtxt {
future_breakage_diagnostics,
fulfilled_expectations,
ice_file: _,
msrv: _,
} = inner.deref_mut();

// For the `Vec`s and `HashMap`s, we overwrite with an empty container to free the
Expand Down Expand Up @@ -1193,6 +1203,7 @@ impl DiagCtxtInner {
future_breakage_diagnostics: Vec::new(),
fulfilled_expectations: Default::default(),
ice_file: None,
msrv: None,
}
}

Expand Down Expand Up @@ -1307,6 +1318,12 @@ impl DiagCtxtInner {
}
}

if let (Some(msrv), Some(diag_msrv)) = (self.msrv, diagnostic.rust_version())
&& diag_msrv > msrv
{
return None;
}

TRACK_DIAGNOSTIC(diagnostic, &mut |mut diagnostic| {
if let Some(code) = diagnostic.code {
self.emitted_diagnostic_codes.insert(code);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_lint_defs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ edition = "2024"

[dependencies]
# tidy-alphabetical-start
rustc_ast = { path = "../rustc_ast" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_error_messages = { path = "../rustc_error_messages" }
rustc_hir_id = { path = "../rustc_hir_id" }
Expand Down
39 changes: 39 additions & 0 deletions compiler/rustc_lint_defs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::borrow::Cow;
use std::fmt::Display;

use rustc_ast::attr::version::RustcVersion;
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::stable_hash::{StableCompare, StableHash, StableHashCtxt, StableHasher};
use rustc_error_messages::{DiagArgValue, IntoDiagArg};
Expand Down Expand Up @@ -305,6 +306,9 @@ pub struct Lint {

/// `true` if this lint is unaffected by `-D warnings`
pub ignore_deny_warnings: bool,

/// Used to avoid lints which would affect MSRV
pub rust_version: Option<RustcVersion>,
}

/// Extra information for a future incompatibility lint.
Expand Down Expand Up @@ -521,7 +525,40 @@ impl Lint {
crate_level_only: false,
eval_always: false,
ignore_deny_warnings: false,
rust_version: None,
}
}

// FIXME(const-hack): This is used so that `declare_lint` can declare an MSRV statically.
// `RustcVersion::parse_str_strict` should ideally be used instead.
pub const fn parse_rust_version(version: &str) -> RustcVersion {
const fn parse_part(input: &mut &[u8]) -> u16 {
let mut val = 0;
let mut idx = 0;
while idx < input.len() {
let v = input[idx];
match v {
b'0'..=b'9' => {
val = val * 10 + (v - b'0') as u16;
}
b'.' => {
idx += 1;
break;
}
_ => panic!("invalid character in version"),
}
idx += 1;
}
*input = input.split_at(idx).1;
val
}

let mut bytes = version.as_bytes();
let major = parse_part(&mut bytes);
let minor = parse_part(&mut bytes);
let patch = parse_part(&mut bytes);
assert!(bytes.is_empty());
RustcVersion { major, minor, patch }
}

/// Gets the lint's name, with ASCII letters converted to lowercase.
Expand Down Expand Up @@ -671,6 +708,7 @@ macro_rules! declare_lint {
$($field:ident : $val:expr),* $(,)*
}; )?
$(@edition $lint_edition:ident => $edition_level:ident;)?
$(@msrv = $msrv:literal;)?
$($v:ident),*) => (
$(#[$attr])*
$vis static $NAME: &$crate::Lint = &$crate::Lint {
Expand All @@ -687,6 +725,7 @@ macro_rules! declare_lint {
}),)?
$(edition_lint_opts: Some(($crate::Edition::$lint_edition, $crate::$edition_level)),)?
$(eval_always: $eval_always,)?
$(rust_version: Some($crate::Lint::parse_rust_version($msrv)),)?
..$crate::Lint::default_fields_for_macro()
};
);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>(
err.disable_suggestions();
}

err.is_lint(lint.name_lower(), has_future_breakage);
err.is_lint(lint.name_lower(), has_future_breakage, lint.rust_version);
// Lint diagnostics that are covered by the expect level will not be emitted outside
// the compiler. It is therefore not necessary to add any information for the user.
// This will therefore directly call the decorate function which will in turn emit
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_public/src/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,16 @@ impl VariantDef {
pub fn fields(&self) -> Vec<FieldDef> {
with(|cx| cx.variant_fields(*self))
}

/// Returns the variant index.
pub fn idx(&self) -> VariantIdx {
self.idx
}

/// Returns the `AdtDef` which this variant comes from.
pub fn adt_def(&self) -> AdtDef {
self.adt_def
}
}

crate_def_with_ty! {
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3059,6 +3059,7 @@ pub(crate) mod dep_tracking {
use std::path::PathBuf;

use rustc_abi::Align;
use rustc_ast::attr::version::RustcVersion;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::stable_hash::StableHasher;
use rustc_errors::LanguageIdentifier;
Expand Down Expand Up @@ -3184,7 +3185,8 @@ pub(crate) mod dep_tracking {
InliningThreshold,
FunctionReturn,
Align,
CodegenRetagOptions
CodegenRetagOptions,
RustcVersion,
);

impl<T1, T2> DepTrackingHash for (T1, T2)
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_session/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ pub fn feature_warn_issue(
// Decorate this as a future-incompatibility lint as in rustc_middle::lint::lint_level
let lint = UNSTABLE_SYNTAX_PRE_EXPANSION;
let future_incompatible = lint.future_incompatible.as_ref().unwrap();
err.is_lint(lint.name_lower(), /* has_future_breakage */ false);
err.is_lint(
lint.name_lower(),
/* has_future_breakage */ false,
/* rust_version */ None,
);
err.warn(lint.desc);
err.note(format!("for more information, see {}", future_incompatible.reason.reference()));

Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::path::PathBuf;
use std::str;

use rustc_abi::Align;
use rustc_ast::attr::version::RustcVersion;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::profiling::TimePassesFormat;
use rustc_data_structures::stable_hash::StableHasher;
Expand Down Expand Up @@ -846,6 +847,7 @@ mod desc {
super::mitigation_coverage::DeniedPartialMitigationKind::KINDS;
pub(crate) const parse_deny_partial_mitigations: &str =
super::mitigation_coverage::DeniedPartialMitigationKind::KINDS;
pub(crate) const parse_rust_version: &str = "a rust version (`xx.yy.zz`)";
}

pub mod parse {
Expand Down Expand Up @@ -2054,6 +2056,18 @@ pub mod parse {
};
true
}

pub(crate) fn parse_rust_version(slot: &mut Option<RustcVersion>, v: Option<&str>) -> bool {
let Some(v) = v else {
return false;
};
if let Some(version) = RustcVersion::parse_str_strict(v) {
*slot = Some(version);
true
} else {
false
}
}
}

options! {
Expand Down Expand Up @@ -2454,6 +2468,8 @@ options! {
"lint LLVM IR (default: no)"),
lint_mir: bool = (false, parse_bool, [UNTRACKED],
"lint MIR before and after each transformation"),
lint_rust_version: Option<RustcVersion> = (None, parse_rust_version, [TRACKED],
"control the minimum rust version for lints"),
llvm_module_flag: Vec<(String, u32, String)> = (Vec::new(), parse_llvm_module_flag, [TRACKED],
"a list of module flags to pass to LLVM (space separated)"),
llvm_plugins: Vec<String> = (Vec::new(), parse_list, [TRACKED],
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_session/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,10 @@ pub fn build_session(
dcx = dcx.with_ice_file(ice_file);
}

if let Some(msrv) = sopts.unstable_opts.lint_rust_version {
dcx = dcx.with_msrv(msrv);
}

let host_triple = TargetTuple::from_tuple(config::host_tuple());
let (host, target_warnings) =
Target::search(&host_triple, sopts.sysroot.path(), sopts.unstable_opts.unstable_options)
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3002,7 +3002,7 @@ impl<T: PointeeSized> Pointer for *const T {
// metadata type to reduce the amount of codegen work needed for each distinct type.
let ptr: *const T = *self;
let ptr_addr = ptr.expose_provenance();
if <<T as core::ptr::Pointee>::Metadata as core::unit::IsUnit>::is_unit() {
if <<T as core::ptr::Pointee>::Metadata as core::unit::IsUnit>::IS_UNIT {
pointer_fmt_inner(ptr_addr, f)
} else {
wide_pointer_fmt_inner(ptr_addr, &core::ptr::metadata(ptr), f)
Expand Down
15 changes: 5 additions & 10 deletions library/core/src/unit.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::intrinsics::type_id;

/// Collapses all unit items from an iterator into one.
///
/// This is more useful when combined with higher-level abstractions, like
Expand All @@ -19,17 +21,10 @@ impl FromIterator<()> for () {
}

pub(crate) trait IsUnit {
fn is_unit() -> bool;
const IS_UNIT: bool;
}

impl<T: ?Sized> IsUnit for T {
default fn is_unit() -> bool {
false
}
}

impl IsUnit for () {
fn is_unit() -> bool {
true
}
// `type_id` erases lifetimes, but that's OK here because "is it ()" never depends on lifetimes
const IS_UNIT: bool = type_id::<Self>() == type_id::<()>();
}
2 changes: 1 addition & 1 deletion src/bootstrap/src/core/download.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1093,7 +1093,7 @@ fn download_http_with_retries(
),
]).run_capture_stdout(exec_ctx);

if powershell.is_failure() {
if powershell.is_success() {
return;
}

Expand Down
1 change: 1 addition & 0 deletions src/tools/compiletest/src/directives/directive_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
"only-powerpc",
"only-riscv32",
"only-riscv64",
"only-riscv64gc-unknown-linux-gnu",
"only-rustc_abi-x86-sse2",
"only-s390x",
"only-sparc",
Expand Down
Loading
Loading