Skip to content
Open
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
46 changes: 37 additions & 9 deletions compiler/rustc_attr_parsing/src/attributes/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc_parse::parser::{ForceCollect, Parser};
use rustc_parse::{exp, parse_in};
use rustc_session::Session;
use rustc_session::config::ExpectedValues;
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
use rustc_session::lint::builtin::{EMPTY_CFG_PREDICATE, UNEXPECTED_CFGS};
use rustc_session::parse::{ParseSess, feature_err};
use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};
use thin_vec::ThinVec;
Expand Down Expand Up @@ -63,14 +63,42 @@ pub fn parse_cfg_entry<S: Stage>(
};
CfgEntry::Not(Box::new(parse_cfg_entry(cx, single)?), list.span)
}
Some(sym::any) => CfgEntry::Any(
list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(),
list.span,
),
Some(sym::all) => CfgEntry::All(
list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(),
list.span,
),
Some(sym::any) => {
if list.is_empty() && !list.span.from_expansion() {
let span = meta.span();
cx.emit_lint(
EMPTY_CFG_PREDICATE,
AttributeLintKind::EmptyCfgPredictate {
predicate_span: span,
predicate: sym::any,
lit: false,
},
span,
);
}
CfgEntry::Any(
list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(),
list.span,
)
}
Some(sym::all) => {
if list.is_empty() && !list.span.from_expansion() {
let span = meta.span();
cx.emit_lint(
EMPTY_CFG_PREDICATE,
AttributeLintKind::EmptyCfgPredictate {
predicate_span: span,
predicate: sym::all,
lit: true,
},
span,
);
}
CfgEntry::All(
list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(),
list.span,
)
}
Some(sym::target) => parse_cfg_entry_target(cx, list, meta.span())?,
Some(sym::version) => parse_cfg_entry_version(cx, list, meta.span())?,
_ => {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_cranelift/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ pub(crate) fn compile_fn(
context.clear();
context.func = codegened_func.func;

#[cfg(any())] // This is never true
#[cfg(false)]
let _clif_guard = {
use std::fmt::Write;

Expand Down
39 changes: 39 additions & 0 deletions compiler/rustc_errors/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::panic;
use std::path::PathBuf;
use std::str::FromStr;
use std::thread::panicking;

use rustc_data_structures::fx::FxIndexMap;
Expand Down Expand Up @@ -263,6 +264,38 @@ pub struct DiagInner {
/// With `-Ztrack_diagnostics` enabled,
/// we print where in rustc this error was emitted.
pub(crate) emitted_at: DiagLocation,
/// Used to avoid lints which would affect MSRV
pub rust_version: Option<RustVersion>,
}

#[derive(Copy, Clone, Debug, Encodable, Decodable, PartialEq, Eq, PartialOrd, Ord)]
pub struct RustVersion {
pub major: u64,
pub minor: u64,
pub patch: u64,
}

impl FromStr for RustVersion {
type Err = ();

fn from_str(s: &str) -> Result<Self, Self::Err> {
fn get_number(s: &str) -> Result<(u64, &str), ()> {
let end = s.chars().take_while(char::is_ascii_digit).count();
if end == 0 {
return Err(());
}
// `is_ascii_digit` ensures that this will be on a char boundary
let (num, rest) = s.split_at(end);
Ok((num.parse().map_err(|_| ())?, rest))
}

let (major, s) = get_number(s)?;
let s = s.strip_prefix(".").ok_or(())?;
let (minor, s) = get_number(s)?;
let s = s.strip_prefix(".").ok_or(())?;
let (patch, _) = get_number(s)?;
Ok(Self { major, minor, patch })
}
}

impl DiagInner {
Expand All @@ -287,6 +320,7 @@ impl DiagInner {
is_lint: None,
long_ty_path: None,
emitted_at: DiagLocation::caller(),
rust_version: None,
}
}

Expand Down Expand Up @@ -377,6 +411,10 @@ impl DiagInner {
self.args = std::mem::take(&mut self.reserved_args);
}

pub fn set_rust_version(&mut self, version: RustVersion) {
self.rust_version = Some(version);
}

pub fn emitted_at_sub_diag(&self) -> Subdiag {
let track = format!("-Ztrack-diagnostics: created at {}", self.emitted_at);
Subdiag {
Expand Down Expand Up @@ -410,6 +448,7 @@ impl DiagInner {
// omit self.sort_span
&self.is_lint,
// omit self.emitted_at
// omit rust_version
)
}
}
Expand Down
13 changes: 12 additions & 1 deletion compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub use codes::*;
pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer};
pub use diagnostic::{
BugAbort, Diag, DiagArgMap, DiagInner, DiagStyledString, Diagnostic, EmissionGuarantee,
FatalAbort, LintDiagnostic, LintDiagnosticBox, StringPart, Subdiag, Subdiagnostic,
FatalAbort, LintDiagnostic, LintDiagnosticBox, RustVersion, StringPart, Subdiag, Subdiagnostic,
};
pub use diagnostic_impls::{
DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter,
Expand Down Expand Up @@ -633,6 +633,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 `CARGO_PKG_RUST_VERSION`; this allows avoiding emitting lints which would raise MSRV.
msrv: Option<RustVersion>,
}

/// A key denoting where from a diagnostic was stashed.
Expand Down Expand Up @@ -822,6 +825,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 @@ -1503,6 +1507,7 @@ impl DiagCtxtInner {
future_breakage_diagnostics: Vec::new(),
fulfilled_expectations: Default::default(),
ice_file: None,
msrv: std::env::var("CARGO_PKG_RUST_VERSION").ok().and_then(|vers| vers.parse().ok()),
}
}

Expand Down Expand Up @@ -1617,6 +1622,12 @@ impl DiagCtxtInner {
}
}

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

TRACK_DIAGNOSTIC(diagnostic, &mut |mut diagnostic| {
if let Some(code) = diagnostic.code {
self.emitted_diagnostic_codes.insert(code);
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ lint_empty_attribute =
*[other] using `{$attr_path}` with an empty list has no effect
}
lint_empty_cfg_predicate = use of empty `cfg({$predicate}())`
.note = this used to be a common pattern before `cfg(true)` and `cfg(false)` were added to the language in Rust 1.88
.suggestion = consider using a boolean literal
-lint_previously_accepted =
this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
lint_enum_intrinsics_mem_discriminant =
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_lint/src/early/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,5 +367,8 @@ pub fn decorate_attribute_lint(
&AttributeLintKind::UnexpectedCfgValue(name, value) => {
check_cfg::unexpected_cfg_value(sess, tcx, name, value).decorate_lint(diag)
}
&AttributeLintKind::EmptyCfgPredictate { predicate_span, predicate, lit } => {
lints::EmptyCfgPredicate { predicate_span, predicate, lit }.decorate_lint(diag)
}
}
}
10 changes: 10 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3137,6 +3137,16 @@ pub(crate) struct EmptyAttributeList {
pub valid_without_list: bool,
}

#[derive(LintDiagnostic)]
#[diag(lint_empty_cfg_predicate, msrv = "1.88.0")]
#[note]
pub(crate) struct EmptyCfgPredicate {
#[suggestion(code = "{lit}", applicability = "machine-applicable", style = "verbose")]
pub predicate_span: Span,
pub predicate: Symbol,
pub lit: bool,
}

#[derive(LintDiagnostic)]
#[diag(lint_invalid_target)]
#[warning]
Expand Down
28 changes: 28 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ declare_lint_pass! {
DUPLICATE_MACRO_ATTRIBUTES,
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
ELIDED_LIFETIMES_IN_PATHS,
EMPTY_CFG_PREDICATE,
EXPLICIT_BUILTIN_CFGS_IN_FLAGS,
EXPORTED_PRIVATE_DEPENDENCIES,
FFI_UNWIND_CALLS,
Expand Down Expand Up @@ -5368,3 +5369,30 @@ declare_lint! {
report_in_deps: false,
};
}

declare_lint! {
/// The `empty_cfg_predicate` lint detects the use of empty `cfg` predicate lists.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![deny(empty_cfg_predicate)]
/// #[cfg(any())]
/// fn foo() {}
///
/// #[cfg(all())]
/// fn bar() {}
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// The meaning of `cfg(any())` and `cfg(all())` is not immediately obvious;
/// `cfg(false)` and `cfg(true)` respectively may be used instead.
/// This used to be a common pattern before `cfg(true)` and `cfg(false)`
/// were added to the language in Rust 1.88
pub EMPTY_CFG_PREDICATE,
Warn,
"detects use of empty `cfg(any())` and `cfg(all())`"
}
8 changes: 8 additions & 0 deletions compiler/rustc_lint_defs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,14 @@ pub enum AttributeLintKind {
},
UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>),
UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>),
EmptyCfgPredictate {
/// Span of `any()`/`all()`
predicate_span: Span,
/// Either `any` or `all`
predicate: Symbol,
/// The boolean literal to replace with
lit: bool,
},
}

pub type RegisteredTools = FxIndexSet<Ident>;
Expand Down
48 changes: 47 additions & 1 deletion compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#![deny(unused_must_use)]

use std::str::FromStr;

use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote, quote_spanned};
use syn::spanned::Spanned;
Expand Down Expand Up @@ -215,9 +217,22 @@ impl DiagnosticDeriveVariantBuilder {
tokens.extend(quote! {
diag.code(#code);
});
} else if path.is_ident("msrv") {
let msrv = nested.parse::<syn::LitStr>()?;
if let Ok(RustVersion { major, minor, patch }) = msrv.value().parse() {
tokens.extend(quote! {
diag.set_rust_version(::rustc_errors::RustVersion {
major: #major,
minor: #minor,
patch: #patch,
});
});
} else {
span_err(msrv.span().unwrap(), "failed to parse rust version").emit();
};
} else {
span_err(path.span().unwrap(), "unknown argument")
.note("only the `code` parameter is valid after the slug")
.note("only `code` or `msrv`are valid after the slug")
.emit();

// consume the buffer so we don't have syntax errors from syn
Expand Down Expand Up @@ -504,3 +519,34 @@ impl DiagnosticDeriveVariantBuilder {
}
}
}

// FIXME: Duplicated in `rustc_errors`
#[derive(Copy, Clone, Debug)]
struct RustVersion {
major: u64,
minor: u64,
patch: u64,
}

impl FromStr for RustVersion {
type Err = ();

fn from_str(s: &str) -> Result<Self, Self::Err> {
fn get_number(s: &str) -> Result<(u64, &str), ()> {
let end = s.chars().take_while(char::is_ascii_digit).count();
if end == 0 {
return Err(());
}
// `is_ascii_digit` ensures that this will be on a char boundary
let (num, rest) = s.split_at(end);
Ok((num.parse().map_err(|_| ())?, rest))
}

let (major, s) = get_number(s)?;
let s = s.strip_prefix(".").ok_or(())?;
let (minor, s) = get_number(s)?;
let s = s.strip_prefix(".").ok_or(())?;
let (patch, _) = get_number(s)?;
Ok(Self { major, minor, patch })
}
}
2 changes: 1 addition & 1 deletion library/core/src/primitive_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,7 @@ impl () {}
/// # pub unsafe fn malloc(_size: usize) -> *mut core::ffi::c_void { core::ptr::NonNull::dangling().as_ptr() }
/// # pub unsafe fn free(_ptr: *mut core::ffi::c_void) {}
/// # }
/// # #[cfg(any())]
/// # #[cfg(false)]
/// #[allow(unused_extern_crates)]
/// extern crate libc;
///
Expand Down
8 changes: 0 additions & 8 deletions src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,6 @@ fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[MetaItemInner]) {
}
},
);
} else if list.is_empty() && meta.has_name(sym::all) {
span_lint_and_then(
cx,
NON_MINIMAL_CFG,
meta.span,
"unneeded sub `cfg` when there is no condition",
|_| {},
);
}
}
}
Expand Down
Loading
Loading