11use rustc_ast:: { LitKind , MetaItem , MetaItemInner , MetaItemKind , MetaItemLit , NodeId } ;
22use rustc_ast_pretty:: pprust;
33use rustc_attr_data_structures:: RustcVersion ;
4+ use rustc_errors:: Applicability ;
45use rustc_feature:: { Features , GatedCfg , find_gated_cfg} ;
56use rustc_session:: Session ;
67use rustc_session:: config:: ExpectedValues ;
@@ -9,6 +10,7 @@ use rustc_session::lint::builtin::UNEXPECTED_CFGS;
910use rustc_session:: parse:: feature_err;
1011use rustc_span:: symbol:: kw;
1112use rustc_span:: { Span , Symbol , sym} ;
13+ use rustc_target:: spec:: apple;
1214
1315use crate :: session_diagnostics:: { self , UnsupportedLiteralReason } ;
1416use crate :: { fluent_generated, parse_version} ;
@@ -149,6 +151,129 @@ pub fn eval_condition(
149151 RustcVersion :: CURRENT >= min_version
150152 }
151153 }
154+ MetaItemKind :: List ( mis) if cfg. name_or_empty ( ) == sym:: os_version_min => {
155+ try_gate_cfg ( sym:: os_version_min, cfg. span , sess, features) ;
156+
157+ let ( platform, version) = match & mis[ ..] {
158+ [ platform, version] => ( platform, version) ,
159+ [ ..] => {
160+ dcx. emit_err ( session_diagnostics:: ExpectedPlatformAndVersionLiterals {
161+ span : cfg. span ,
162+ } ) ;
163+ return false ;
164+ }
165+ } ;
166+
167+ let ( platform_sym, platform_span) = match platform {
168+ MetaItemInner :: Lit ( MetaItemLit {
169+ kind : LitKind :: Str ( platform_sym, ..) ,
170+ span : platform_span,
171+ ..
172+ } ) => ( platform_sym, platform_span) ,
173+ MetaItemInner :: Lit ( MetaItemLit { span, .. } )
174+ | MetaItemInner :: MetaItem ( MetaItem { span, .. } ) => {
175+ dcx. emit_err ( session_diagnostics:: ExpectedPlatformLiteral { span : * span } ) ;
176+ return false ;
177+ }
178+ } ;
179+
180+ let ( version_sym, version_span) = match version {
181+ MetaItemInner :: Lit ( MetaItemLit {
182+ kind : LitKind :: Str ( version_sym, ..) ,
183+ span : version_span,
184+ ..
185+ } ) => ( version_sym, version_span) ,
186+ MetaItemInner :: Lit ( MetaItemLit { span, .. } )
187+ | MetaItemInner :: MetaItem ( MetaItem { span, .. } ) => {
188+ dcx. emit_err ( session_diagnostics:: ExpectedVersionLiteral { span : * span } ) ;
189+ return false ;
190+ }
191+ } ;
192+
193+ // Always parse version, regardless of current target platform.
194+ let version = match * platform_sym {
195+ // Apple platforms follow the same versioning schema.
196+ sym:: macos | sym:: ios | sym:: tvos | sym:: watchos | sym:: visionos => {
197+ match version_sym. as_str ( ) . parse ( ) {
198+ Ok ( version) => {
199+ let os_min = apple:: OSVersion :: os_minimum_deployment_target (
200+ & platform_sym. as_str ( ) ,
201+ ) ;
202+
203+ // It's unnecessary to specify `cfg_target_os(...)` for a platform
204+ // version that is lower than the minimum targetted by `rustc` (instead,
205+ // make the item always available).
206+ //
207+ // This is correct _now_, but once we bump versions next time, we should
208+ // maybe make this a lint so that users can opt-in to supporting older
209+ // `rustc` versions? Or perhaps only fire the warning when Cargo's
210+ // `rust-version` field is above the version where the bump happened? Or
211+ // perhaps keep the version we check against low for a sufficiently long
212+ // time?
213+ if version <= os_min {
214+ sess. dcx ( )
215+ . create_warn (
216+ session_diagnostics:: AppleVersionUnnecessarilyLow {
217+ span : * version_span,
218+ os_min : os_min. fmt_pretty ( ) . to_string ( ) ,
219+ } ,
220+ )
221+ . with_span_suggestion (
222+ cfg. span ,
223+ "use `target_os` instead" ,
224+ format ! ( "target_os = \" {platform_sym}\" " ) ,
225+ Applicability :: MachineApplicable ,
226+ )
227+ . emit ( ) ;
228+ }
229+
230+ PlatformVersion :: Apple { os : * platform_sym, version }
231+ }
232+ Err ( error) => {
233+ sess. dcx ( ) . emit_err ( session_diagnostics:: AppleVersionInvalid {
234+ span : * version_span,
235+ error,
236+ } ) ;
237+ return false ;
238+ }
239+ }
240+ }
241+ // FIXME(madsmtm): Handle further platforms as specified in the RFC.
242+ sym:: windows | sym:: libc => {
243+ #[ allow( rustc:: untranslatable_diagnostic) ] // Temporary
244+ dcx. span_err ( * platform_span, "unimplemented platform" ) ;
245+ return false ;
246+ }
247+ _ => {
248+ // Unknown platform. This is intentionally a warning (and not an error) to be
249+ // future-compatible with later additions.
250+ let known_platforms = [
251+ sym:: macos,
252+ sym:: ios,
253+ sym:: tvos,
254+ sym:: watchos,
255+ sym:: visionos,
256+ // sym::windows,
257+ // sym::libc,
258+ ] ;
259+ dcx. emit_warn ( session_diagnostics:: UnknownPlatformLiteral {
260+ span : * platform_span,
261+ possibilities : known_platforms. into_iter ( ) . collect ( ) ,
262+ } ) ;
263+ return false ;
264+ }
265+ } ;
266+
267+ // Figure out actual cfg-status based on current platform.
268+ match version {
269+ PlatformVersion :: Apple { os, version } if os. as_str ( ) == sess. target . os => {
270+ let deployment_target = sess. apple_deployment_target ( ) ;
271+ version <= deployment_target
272+ }
273+ // If a `cfg`-value does not apply to a specific platform, assume
274+ _ => false ,
275+ }
276+ }
152277 MetaItemKind :: List ( mis) => {
153278 for mi in mis. iter ( ) {
154279 if mi. meta_item_or_bool ( ) . is_none ( ) {
@@ -245,3 +370,8 @@ pub fn eval_condition(
245370 }
246371 }
247372}
373+
374+ #[ derive( Debug , Clone , PartialEq , Eq , Hash ) ]
375+ enum PlatformVersion {
376+ Apple { os : Symbol , version : apple:: OSVersion } ,
377+ }
0 commit comments