From 00bdfcc077548b64c62758168c9b8d8af526aff9 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 11 Feb 2025 14:20:58 +0100 Subject: [PATCH] Add cfg_os_version_min feature Based on in-progress RFC: https://github.com/rust-lang/rfcs/pull/3750. Only implemented for Apple platforms for now, but written in a way that should be easily expandable to include other platforms. --- Cargo.lock | 1 + compiler/rustc_attr_parsing/Cargo.toml | 1 + compiler/rustc_attr_parsing/messages.ftl | 15 ++ .../rustc_attr_parsing/src/attributes/cfg.rs | 130 ++++++++++++++++ .../src/session_diagnostics.rs | 44 +++++- compiler/rustc_feature/src/builtin_attrs.rs | 1 + compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_span/src/symbol.rs | 7 + .../language-features/cfg-os-version-min.md | 107 ++++++++++++++ .../cfg-os-version-min-deployment-target.rs | 21 +++ tests/ui/cfg/cfg-os-version-min-edges.rs | 12 ++ tests/ui/cfg/cfg-os-version-min-edges.stderr | 8 + tests/ui/cfg/cfg-os-version-min-invalid.rs | 57 +++++++ .../ui/cfg/cfg-os-version-min-invalid.stderr | 139 ++++++++++++++++++ .../feature-gate-cfg-os-version-min.rs | 18 +++ .../feature-gate-cfg-os-version-min.stderr | 39 +++++ 16 files changed, 600 insertions(+), 2 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/cfg-os-version-min.md create mode 100644 tests/ui/cfg/cfg-os-version-min-deployment-target.rs create mode 100644 tests/ui/cfg/cfg-os-version-min-edges.rs create mode 100644 tests/ui/cfg/cfg-os-version-min-edges.stderr create mode 100644 tests/ui/cfg/cfg-os-version-min-invalid.rs create mode 100644 tests/ui/cfg/cfg-os-version-min-invalid.stderr create mode 100644 tests/ui/feature-gates/feature-gate-cfg-os-version-min.rs create mode 100644 tests/ui/feature-gates/feature-gate-cfg-os-version-min.stderr diff --git a/Cargo.lock b/Cargo.lock index 99bd1bff02045..2573b75de524b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3348,6 +3348,7 @@ dependencies = [ "rustc_macros", "rustc_session", "rustc_span", + "rustc_target", "thin-vec", ] diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index 32029137268ba..0fc38a661ca25 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -17,5 +17,6 @@ rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } thin-vec = "0.2.12" # tidy-alphabetical-end diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 45174c9582d33..4bb75c06a43e1 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -1,3 +1,9 @@ +attr_parsing_apple_version_invalid = + failed parsing version: {$error} + +attr_parsing_apple_version_unnecessarily_low = + version is set unnecessarily low, the minimum supported by Rust on this platform is {$os_min} + attr_parsing_cfg_predicate_identifier = `cfg` predicate key must be an identifier @@ -11,6 +17,12 @@ attr_parsing_empty_confusables = attr_parsing_expected_one_cfg_pattern = expected 1 cfg-pattern +attr_parsing_expected_platform_and_version_literals = + expected two literals, a platform and a version + +attr_parsing_expected_platform_literal = + expected a platform literal + attr_parsing_expected_single_version_literal = expected single version literal @@ -108,6 +120,9 @@ attr_parsing_unknown_meta_item = unknown meta item '{$item}' .label = expected one of {$expected} +attr_parsing_unknown_platform_literal = + unknown platform literal, expected values are: {$possibilities} + attr_parsing_unknown_version_literal = unknown version literal format, assuming it refers to a future version diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 0d6d521b40c61..bbf5f2add10ab 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -1,6 +1,7 @@ use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId}; use rustc_ast_pretty::pprust; use rustc_attr_data_structures::RustcVersion; +use rustc_errors::Applicability; use rustc_feature::{Features, GatedCfg, find_gated_cfg}; use rustc_session::Session; use rustc_session::config::ExpectedValues; @@ -9,6 +10,7 @@ use rustc_session::lint::builtin::UNEXPECTED_CFGS; use rustc_session::parse::feature_err; use rustc_span::symbol::kw; use rustc_span::{Span, Symbol, sym}; +use rustc_target::spec::apple; use crate::session_diagnostics::{self, UnsupportedLiteralReason}; use crate::{fluent_generated, parse_version}; @@ -149,6 +151,129 @@ pub fn eval_condition( RustcVersion::CURRENT >= min_version } } + MetaItemKind::List(mis) if cfg.name_or_empty() == sym::os_version_min => { + try_gate_cfg(sym::os_version_min, cfg.span, sess, features); + + let (platform, version) = match &mis[..] { + [platform, version] => (platform, version), + [..] => { + dcx.emit_err(session_diagnostics::ExpectedPlatformAndVersionLiterals { + span: cfg.span, + }); + return false; + } + }; + + let (platform_sym, platform_span) = match platform { + MetaItemInner::Lit(MetaItemLit { + kind: LitKind::Str(platform_sym, ..), + span: platform_span, + .. + }) => (platform_sym, platform_span), + MetaItemInner::Lit(MetaItemLit { span, .. }) + | MetaItemInner::MetaItem(MetaItem { span, .. }) => { + dcx.emit_err(session_diagnostics::ExpectedPlatformLiteral { span: *span }); + return false; + } + }; + + let (version_sym, version_span) = match version { + MetaItemInner::Lit(MetaItemLit { + kind: LitKind::Str(version_sym, ..), + span: version_span, + .. + }) => (version_sym, version_span), + MetaItemInner::Lit(MetaItemLit { span, .. }) + | MetaItemInner::MetaItem(MetaItem { span, .. }) => { + dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span }); + return false; + } + }; + + // Always parse version, regardless of current target platform. + let version = match *platform_sym { + // Apple platforms follow the same versioning schema. + sym::macos | sym::ios | sym::tvos | sym::watchos | sym::visionos => { + match version_sym.as_str().parse() { + Ok(version) => { + let os_min = apple::OSVersion::os_minimum_deployment_target( + &platform_sym.as_str(), + ); + + // It's unnecessary to specify `cfg_target_os(...)` for a platform + // version that is lower than the minimum targetted by `rustc` (instead, + // make the item always available). + // + // This is correct _now_, but once we bump versions next time, we should + // maybe make this a lint so that users can opt-in to supporting older + // `rustc` versions? Or perhaps only fire the warning when Cargo's + // `rust-version` field is above the version where the bump happened? Or + // perhaps keep the version we check against low for a sufficiently long + // time? + if version <= os_min { + sess.dcx() + .create_warn( + session_diagnostics::AppleVersionUnnecessarilyLow { + span: *version_span, + os_min: os_min.fmt_pretty().to_string(), + }, + ) + .with_span_suggestion( + cfg.span, + "use `target_os` instead", + format!("target_os = \"{platform_sym}\""), + Applicability::MachineApplicable, + ) + .emit(); + } + + PlatformVersion::Apple { os: *platform_sym, version } + } + Err(error) => { + sess.dcx().emit_err(session_diagnostics::AppleVersionInvalid { + span: *version_span, + error, + }); + return false; + } + } + } + // FIXME(madsmtm): Handle further platforms as specified in the RFC. + sym::windows | sym::libc => { + #[allow(rustc::untranslatable_diagnostic)] // Temporary + dcx.span_err(*platform_span, "unimplemented platform"); + return false; + } + _ => { + // Unknown platform. This is intentionally a warning (and not an error) to be + // future-compatible with later additions. + let known_platforms = [ + sym::macos, + sym::ios, + sym::tvos, + sym::watchos, + sym::visionos, + // sym::windows, + // sym::libc, + ]; + dcx.emit_warn(session_diagnostics::UnknownPlatformLiteral { + span: *platform_span, + possibilities: known_platforms.into_iter().collect(), + }); + return false; + } + }; + + // Figure out actual cfg-status based on current platform. + match version { + PlatformVersion::Apple { os, version } if os.as_str() == sess.target.os => { + let deployment_target = sess.apple_deployment_target(); + version <= deployment_target + } + // If a `cfg`-value does not apply to a specific platform, assume + _ => false, + } + } MetaItemKind::List(mis) => { for mi in mis.iter() { if mi.meta_item_or_bool().is_none() { @@ -245,3 +370,8 @@ pub fn eval_condition( } } } + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum PlatformVersion { + Apple { os: Symbol, version: apple::OSVersion }, +} diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 9d34b807ac2fe..e65fabacb5f3a 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -1,8 +1,10 @@ -use std::num::IntErrorKind; +use std::num::{IntErrorKind, ParseIntError}; use rustc_ast as ast; use rustc_errors::codes::*; -use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level}; +use rustc_errors::{ + Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, +}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; @@ -16,6 +18,22 @@ pub(crate) enum UnsupportedLiteralReason { DeprecatedKvPair, } +#[derive(Diagnostic)] +#[diag(attr_parsing_apple_version_invalid)] +pub(crate) struct AppleVersionInvalid { + #[primary_span] + pub span: Span, + pub error: ParseIntError, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_apple_version_unnecessarily_low)] +pub(crate) struct AppleVersionUnnecessarilyLow { + #[primary_span] + pub span: Span, + pub os_min: String, +} + #[derive(Diagnostic)] #[diag(attr_parsing_expected_one_cfg_pattern, code = E0536)] pub(crate) struct ExpectedOneCfgPattern { @@ -386,6 +404,20 @@ pub(crate) struct DeprecatedItemSuggestion { pub details: (), } +#[derive(Diagnostic)] +#[diag(attr_parsing_expected_platform_and_version_literals)] +pub(crate) struct ExpectedPlatformAndVersionLiterals { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_expected_platform_literal)] +pub(crate) struct ExpectedPlatformLiteral { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(attr_parsing_expected_single_version_literal)] pub(crate) struct ExpectedSingleVersionLiteral { @@ -432,6 +464,14 @@ pub(crate) struct SoftNoArgs { pub span: Span, } +#[derive(Diagnostic)] +#[diag(attr_parsing_unknown_platform_literal)] +pub(crate) struct UnknownPlatformLiteral { + #[primary_span] + pub span: Span, + pub possibilities: DiagSymbolList, +} + #[derive(Diagnostic)] #[diag(attr_parsing_unknown_version_literal)] pub(crate) struct UnknownVersionLiteral { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 1e33e2e9393f7..bbe734e1e927d 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -33,6 +33,7 @@ const GATED_CFGS: &[GatedCfg] = &[ ), (sym::sanitize, sym::cfg_sanitize, Features::cfg_sanitize), (sym::version, sym::cfg_version, Features::cfg_version), + (sym::os_version_min, sym::cfg_os_version_min, Features::cfg_os_version_min), (sym::relocation_model, sym::cfg_relocation_model, Features::cfg_relocation_model), (sym::sanitizer_cfi_generalize_pointers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi), (sym::sanitizer_cfi_normalize_integers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 710e129b609fe..eecce4d1528dc 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -395,6 +395,8 @@ declare_features! ( (unstable, cfg_boolean_literals, "1.83.0", Some(131204)), /// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled. (unstable, cfg_contract_checks, "1.86.0", Some(128044)), + /// Allow conditional compilation depending on target platform version. + (unstable, cfg_os_version_min, "CURRENT_RUSTC_VERSION", Some(136866)), /// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour. (unstable, cfg_overflow_checks, "1.71.0", Some(111466)), /// Provides the relocation model information as cfg entry diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 31847ae3b4658..c7577fee483d9 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -611,6 +611,7 @@ symbols! { cfg_eval, cfg_fmt_debug, cfg_hide, + cfg_os_version_min, cfg_overflow_checks, cfg_panic, cfg_relocation_model, @@ -1194,6 +1195,7 @@ symbols! { intrinsics_unaligned_volatile_store, io_stderr, io_stdout, + ios, irrefutable_let_patterns, is, is_val_statically_known, @@ -1279,6 +1281,7 @@ symbols! { loop_break_value, lt, m68k_target_feature, + macos, macro_at_most_once_rep, macro_attributes_in_derive_output, macro_escape, @@ -1502,6 +1505,7 @@ symbols! { ord_cmp_method, os_str_to_os_string, os_string_as_os_str, + os_version_min, other, out, overflow_checks, @@ -2124,6 +2128,7 @@ symbols! { tuple, tuple_indexing, tuple_trait, + tvos, two_phase, ty, type_alias_enum_variants, @@ -2270,6 +2275,7 @@ symbols! { vfp2, vis, visible_private_types, + visionos, volatile, volatile_copy_memory, volatile_copy_nonoverlapping_memory, @@ -2286,6 +2292,7 @@ symbols! { wasm_abi, wasm_import_module, wasm_target_feature, + watchos, where_clause_attrs, while_let, width, diff --git a/src/doc/unstable-book/src/language-features/cfg-os-version-min.md b/src/doc/unstable-book/src/language-features/cfg-os-version-min.md new file mode 100644 index 0000000000000..193ea90e4b596 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/cfg-os-version-min.md @@ -0,0 +1,107 @@ +# `cfg_os_version_min` + +The tracking issue for this feature is: [#136866] + +[#136866]: https://github.com/rust-lang/rust/issues/136866 + +------------------------ + +The `cfg_os_version_min` feature makes it possible to conditionally compile +code depending on the target platform version(s). + +As `cfg(os_version_min("platform", "version"))` is platform-specific, it +internally contains relevant `cfg(target_os = "platform")`. So specifying +`cfg(os_version_min("macos", "1.0"))` is equivalent to +`cfg(target_os = "macos")`. + +Note that this only concerns the compile-time configured version; at runtime, +the version may be higher. + + +## Changing the version + +Each Rust target has a default set of targetted platform versions. Examples +include the operating system version (`windows`, `macos`, Linux `kernel`) and +system library version (`libc`). + +The mechanism for changing a targetted platform version is currently +target-specific (a more general mechanism may be created in the future): +- On macOS, you can select the minimum OS version using the + `MACOSX_DEPLOYMENT_TARGET` environment variable. Similarly for iOS, tvOS, + watchOS and visionOS, see the relevant [platform docs] for details. +- Others: Unknown. + +[platform docs]: https://doc.rust-lang.org/nightly/rustc/platform-support.html + + +## Examples + +Statically link the `preadv` symbol if known to be available on Apple +platforms and fall back to weak linking if not. + +```rust,edition2021 +#![feature(cfg_os_version_min)] +use core::ffi::c_int; +# +# #[allow(non_camel_case_types)] +# mod libc { // Fake libc for example +# use core::ffi::{c_char, c_int, c_void}; +# +# pub type off_t = i64; +# pub type iovec = c_void; // Fake typedef for example +# pub type ssize_t = isize; +# +# pub const RTLD_DEFAULT: *mut c_void = -2isize as *mut c_void; +# +# unsafe extern "C" { +# pub unsafe fn dlsym( +# handle: *mut c_void, +# symbol: *const c_char, +# ) -> *mut c_void; +# +# pub unsafe fn preadv( +# fd: c_int, +# iovec: *const iovec, +# n_iovec: c_int, +# offset: off_t, +# ) -> ssize_t; +# } +# } +# +# // Only test Apple targets for now. +# #[cfg(target_vendor = "apple")] { + +// Always available under these conditions. +#[cfg(any( + os_version_min("macos", "11.0"), + os_version_min("ios", "14.0"), + os_version_min("tvos", "14.0"), + os_version_min("watchos", "7.0"), + os_version_min("visionos", "1.0"), +))] +let preadv = Some(libc::preadv); + +// Otherwise, `preadv` needs to be weakly linked. +#[cfg(not(any( + os_version_min("macos", "11.0"), + os_version_min("ios", "14.0"), + os_version_min("tvos", "14.0"), + os_version_min("watchos", "7.0"), + os_version_min("visionos", "1.0"), +)))] +let preadv = { + // SAFETY: The C string is valid. + let ptr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c"preadv".as_ptr()) }; + type Fn = unsafe extern "C" fn(c_int, *const libc::iovec, c_int, libc::off_t) -> libc::ssize_t; + // SAFETY: The function signature is correct, and the pointer is nullable. + unsafe { core::mem::transmute::<*mut core::ffi::c_void, Option>(ptr) } +}; + +if let Some(preadv) = preadv { + # #[cfg(needs_to_pass_correct_arguments)] + preadv(..) // Use preadv, it's available +} else { + // ... fallback impl +} +# } +``` diff --git a/tests/ui/cfg/cfg-os-version-min-deployment-target.rs b/tests/ui/cfg/cfg-os-version-min-deployment-target.rs new file mode 100644 index 0000000000000..3a80a2512a998 --- /dev/null +++ b/tests/ui/cfg/cfg-os-version-min-deployment-target.rs @@ -0,0 +1,21 @@ +//! Test the semantics of `cfg(os_version_min)` while setting deployment target. + +//@ only-macos +//@ revisions: no_env env_low env_mid env_high +//@[no_env] unset-rustc-env:MACOSX_DEPLOYMENT_TARGET +//@[env_low] rustc-env:MACOSX_DEPLOYMENT_TARGET=10.14 +//@[env_mid] rustc-env:MACOSX_DEPLOYMENT_TARGET=14.0 +//@[env_high] rustc-env:MACOSX_DEPLOYMENT_TARGET=17.0 +//@ run-pass + +#![feature(cfg_os_version_min)] + +fn main() { + assert_eq!(cfg!(os_version_min("macos", "14.0")), cfg!(any(env_mid, env_high))); + + // Aarch64 minimum is macOS 11.0, even if a lower env is requested. + assert_eq!( + cfg!(os_version_min("macos", "11.0")), + cfg!(any(env_mid, env_high, target_arch = "aarch64")) + ); +} diff --git a/tests/ui/cfg/cfg-os-version-min-edges.rs b/tests/ui/cfg/cfg-os-version-min-edges.rs new file mode 100644 index 0000000000000..35dfa85d22871 --- /dev/null +++ b/tests/ui/cfg/cfg-os-version-min-edges.rs @@ -0,0 +1,12 @@ +//! Test low and high version with `cfg(os_version_min)`. +//@ run-pass +#![feature(cfg_os_version_min)] + +fn main() { + // Always available on macOS + assert_eq!(cfg!(os_version_min("macos", "10.0")), cfg!(target_os = "macos")); + //~^ WARNING: version is set unnecessarily low + + // Never available + assert!(cfg!(not(os_version_min("macos", "9999.99.99")))); +} diff --git a/tests/ui/cfg/cfg-os-version-min-edges.stderr b/tests/ui/cfg/cfg-os-version-min-edges.stderr new file mode 100644 index 0000000000000..1225f63084a78 --- /dev/null +++ b/tests/ui/cfg/cfg-os-version-min-edges.stderr @@ -0,0 +1,8 @@ +warning: version is set unnecessarily low, the minimum supported by Rust on this platform is 10.12 + --> $DIR/cfg-os-version-min-edges.rs:7:45 + | +LL | assert_eq!(cfg!(os_version_min("macos", "10.0")), cfg!(target_os = "macos")); + | ------------------------^^^^^^- help: use `target_os` instead: `target_os = "macos"` + +warning: 1 warning emitted + diff --git a/tests/ui/cfg/cfg-os-version-min-invalid.rs b/tests/ui/cfg/cfg-os-version-min-invalid.rs new file mode 100644 index 0000000000000..c6da61e956e66 --- /dev/null +++ b/tests/ui/cfg/cfg-os-version-min-invalid.rs @@ -0,0 +1,57 @@ +//! Test invalid syntax for `cfg(os_version_min)`. +#![feature(cfg_os_version_min)] + +#[cfg(os_version_min = "macos")] //~ WARNING: unexpected `cfg` condition name: `os_version_min` +fn foo() {} + +#[cfg(os_version_min("macos"))] //~ ERROR: expected two literals, a platform and a version +fn foo() {} +#[cfg(os_version_min("1", "2", "3"))] //~ ERROR: expected two literals, a platform and a version +fn foo() {} + +#[cfg(os_version_min(macos, "1.0.0"))] //~ ERROR: expected a platform literal +fn foo() {} +#[cfg(os_version_min(42, "1.0.0"))] //~ ERROR: expected a platform literal +fn foo() {} + +#[cfg(os_version_min("macos", 42))] //~ ERROR: expected a version literal +fn foo() {} +#[cfg(os_version_min("macos", 10.10))] //~ ERROR: expected a version literal +fn foo() {} +#[cfg(os_version_min("macos", false))] //~ ERROR: expected a version literal +fn foo() {} + +#[cfg(os_version_min("11.0", "macos"))] //~ WARNING: unknown platform literal +fn foo() {} +#[cfg(os_version_min("linux", "5.3"))] //~ WARNING: unknown platform literal +fn foo() {} + +#[cfg(os_version_min("windows", "10.0.10240"))] //~ ERROR: unimplemented platform +fn foo() {} + +#[cfg(os_version_min("macos", "99999"))] //~ ERROR: failed parsing version +fn foo() {} +#[cfg(os_version_min("macos", "-1"))] //~ ERROR: failed parsing version +fn foo() {} +#[cfg(os_version_min("macos", "65536"))] //~ ERROR: failed parsing version +fn foo() {} +#[cfg(os_version_min("macos", "1.2.3.4"))] //~ ERROR: failed parsing version +fn foo() {} + +#[cfg(os_version_min("macos", "10.0"))] //~ WARNING: version is set unnecessarily low +fn bar1() {} +#[cfg(os_version_min("macos", "0"))] //~ WARNING: version is set unnecessarily low +fn bar2() {} + +#[cfg(os_version_min("macos", "10.12"))] //~ WARNING: version is set unnecessarily low +fn bar3() {} +#[cfg(os_version_min("ios", "10.0"))] //~ WARNING: version is set unnecessarily low +fn bar4() {} +#[cfg(os_version_min("tvos", "10.0"))] //~ WARNING: version is set unnecessarily low +fn bar5() {} +#[cfg(os_version_min("watchos", "5.0"))] //~ WARNING: version is set unnecessarily low +fn bar6() {} +#[cfg(os_version_min("visionos", "1.0"))] //~ WARNING: version is set unnecessarily low +fn bar7() {} + +fn main() {} diff --git a/tests/ui/cfg/cfg-os-version-min-invalid.stderr b/tests/ui/cfg/cfg-os-version-min-invalid.stderr new file mode 100644 index 0000000000000..7946edafdc08a --- /dev/null +++ b/tests/ui/cfg/cfg-os-version-min-invalid.stderr @@ -0,0 +1,139 @@ +error: expected two literals, a platform and a version + --> $DIR/cfg-os-version-min-invalid.rs:7:7 + | +LL | #[cfg(os_version_min("macos"))] + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected two literals, a platform and a version + --> $DIR/cfg-os-version-min-invalid.rs:9:7 + | +LL | #[cfg(os_version_min("1", "2", "3"))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected a platform literal + --> $DIR/cfg-os-version-min-invalid.rs:12:22 + | +LL | #[cfg(os_version_min(macos, "1.0.0"))] + | ^^^^^ + +error: expected a platform literal + --> $DIR/cfg-os-version-min-invalid.rs:14:22 + | +LL | #[cfg(os_version_min(42, "1.0.0"))] + | ^^ + +error: expected a version literal + --> $DIR/cfg-os-version-min-invalid.rs:17:31 + | +LL | #[cfg(os_version_min("macos", 42))] + | ^^ + +error: expected a version literal + --> $DIR/cfg-os-version-min-invalid.rs:19:31 + | +LL | #[cfg(os_version_min("macos", 10.10))] + | ^^^^^ + +error: expected a version literal + --> $DIR/cfg-os-version-min-invalid.rs:21:31 + | +LL | #[cfg(os_version_min("macos", false))] + | ^^^^^ + +warning: unknown platform literal, expected values are: `macos`, `ios`, `tvos`, `watchos`, and `visionos` + --> $DIR/cfg-os-version-min-invalid.rs:24:22 + | +LL | #[cfg(os_version_min("11.0", "macos"))] + | ^^^^^^ + +warning: unknown platform literal, expected values are: `macos`, `ios`, `tvos`, `watchos`, and `visionos` + --> $DIR/cfg-os-version-min-invalid.rs:26:22 + | +LL | #[cfg(os_version_min("linux", "5.3"))] + | ^^^^^^^ + +error: unimplemented platform + --> $DIR/cfg-os-version-min-invalid.rs:29:22 + | +LL | #[cfg(os_version_min("windows", "10.0.10240"))] + | ^^^^^^^^^ + +error: failed parsing version: number too large to fit in target type + --> $DIR/cfg-os-version-min-invalid.rs:32:31 + | +LL | #[cfg(os_version_min("macos", "99999"))] + | ^^^^^^^ + +error: failed parsing version: invalid digit found in string + --> $DIR/cfg-os-version-min-invalid.rs:34:31 + | +LL | #[cfg(os_version_min("macos", "-1"))] + | ^^^^ + +error: failed parsing version: number too large to fit in target type + --> $DIR/cfg-os-version-min-invalid.rs:36:31 + | +LL | #[cfg(os_version_min("macos", "65536"))] + | ^^^^^^^ + +error: failed parsing version: invalid digit found in string + --> $DIR/cfg-os-version-min-invalid.rs:38:31 + | +LL | #[cfg(os_version_min("macos", "1.2.3.4"))] + | ^^^^^^^^^ + +warning: version is set unnecessarily low, the minimum supported by Rust on this platform is 10.12 + --> $DIR/cfg-os-version-min-invalid.rs:41:31 + | +LL | #[cfg(os_version_min("macos", "10.0"))] + | ------------------------^^^^^^- help: use `target_os` instead: `target_os = "macos"` + +warning: version is set unnecessarily low, the minimum supported by Rust on this platform is 10.12 + --> $DIR/cfg-os-version-min-invalid.rs:43:31 + | +LL | #[cfg(os_version_min("macos", "0"))] + | ------------------------^^^- help: use `target_os` instead: `target_os = "macos"` + +warning: version is set unnecessarily low, the minimum supported by Rust on this platform is 10.12 + --> $DIR/cfg-os-version-min-invalid.rs:46:31 + | +LL | #[cfg(os_version_min("macos", "10.12"))] + | ------------------------^^^^^^^- help: use `target_os` instead: `target_os = "macos"` + +warning: version is set unnecessarily low, the minimum supported by Rust on this platform is 10.0 + --> $DIR/cfg-os-version-min-invalid.rs:48:29 + | +LL | #[cfg(os_version_min("ios", "10.0"))] + | ----------------------^^^^^^- help: use `target_os` instead: `target_os = "ios"` + +warning: version is set unnecessarily low, the minimum supported by Rust on this platform is 10.0 + --> $DIR/cfg-os-version-min-invalid.rs:50:30 + | +LL | #[cfg(os_version_min("tvos", "10.0"))] + | -----------------------^^^^^^- help: use `target_os` instead: `target_os = "tvos"` + +warning: version is set unnecessarily low, the minimum supported by Rust on this platform is 5.0 + --> $DIR/cfg-os-version-min-invalid.rs:52:33 + | +LL | #[cfg(os_version_min("watchos", "5.0"))] + | --------------------------^^^^^- help: use `target_os` instead: `target_os = "watchos"` + +warning: version is set unnecessarily low, the minimum supported by Rust on this platform is 1.0 + --> $DIR/cfg-os-version-min-invalid.rs:54:34 + | +LL | #[cfg(os_version_min("visionos", "1.0"))] + | ---------------------------^^^^^- help: use `target_os` instead: `target_os = "visionos"` + +warning: unexpected `cfg` condition name: `os_version_min` + --> $DIR/cfg-os-version-min-invalid.rs:4:7 + | +LL | #[cfg(os_version_min = "macos")] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expected names are: `FALSE` and `test` and 31 more + = help: to expect this configuration use `--check-cfg=cfg(os_version_min, values("macos"))` + = note: see for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default + +error: aborting due to 12 previous errors; 10 warnings emitted + diff --git a/tests/ui/feature-gates/feature-gate-cfg-os-version-min.rs b/tests/ui/feature-gates/feature-gate-cfg-os-version-min.rs new file mode 100644 index 0000000000000..f14d6cb985601 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-os-version-min.rs @@ -0,0 +1,18 @@ +#![feature(cfg_boolean_literals)] + +#[cfg(os_version_min("macos", "11.0"))] +//~^ ERROR `cfg(os_version_min)` is experimental and subject to change +fn foo1() {} + +#[cfg(os_version_min("macos", 1.20))] //~ ERROR: expected a version literal +//~^ ERROR `cfg(os_version_min)` is experimental and subject to change +fn foo2() {} + +// No warning if cfg'd away. +#[cfg_attr(false, cfg(os_version_min("macos", false)))] +fn foo3() {} + +fn main() { + if cfg!(os_version_min("macos", "11.0")) {} + //~^ ERROR `cfg(os_version_min)` is experimental and subject to change +} diff --git a/tests/ui/feature-gates/feature-gate-cfg-os-version-min.stderr b/tests/ui/feature-gates/feature-gate-cfg-os-version-min.stderr new file mode 100644 index 0000000000000..7588469e0f4bc --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-os-version-min.stderr @@ -0,0 +1,39 @@ +error[E0658]: `cfg(os_version_min)` is experimental and subject to change + --> $DIR/feature-gate-cfg-os-version-min.rs:3:7 + | +LL | #[cfg(os_version_min("macos", "11.0"))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #136866 for more information + = help: add `#![feature(cfg_os_version_min)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `cfg(os_version_min)` is experimental and subject to change + --> $DIR/feature-gate-cfg-os-version-min.rs:7:7 + | +LL | #[cfg(os_version_min("macos", 1.20))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #136866 for more information + = help: add `#![feature(cfg_os_version_min)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: expected a version literal + --> $DIR/feature-gate-cfg-os-version-min.rs:7:31 + | +LL | #[cfg(os_version_min("macos", 1.20))] + | ^^^^ + +error[E0658]: `cfg(os_version_min)` is experimental and subject to change + --> $DIR/feature-gate-cfg-os-version-min.rs:16:13 + | +LL | if cfg!(os_version_min("macos", "11.0")) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #136866 for more information + = help: add `#![feature(cfg_os_version_min)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`.