@@ -266,13 +266,18 @@ pub fn find_stability(
266
266
) -> Option < ( Stability , StabilitySpans ) > {
267
267
let mut level: Option < StabilityLevel > = None ;
268
268
let mut stab_spans = StabilitySpans ( smallvec ! [ ] ) ;
269
+ let mut features = smallvec ! [ ] ;
269
270
let mut allowed_through_unstable_modules = false ;
270
271
271
272
for attr in attrs {
272
273
match attr. name_or_empty ( ) {
273
274
sym:: rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true ,
274
- sym:: unstable => try_add_unstability ( sess, attr, & mut level, & mut stab_spans) ,
275
- sym:: stable => try_add_stability ( sess, attr, & mut level, & mut stab_spans) ,
275
+ sym:: unstable => {
276
+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features, parse_unstability)
277
+ }
278
+ sym:: stable => {
279
+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features, parse_stability)
280
+ }
276
281
_ => { }
277
282
}
278
283
}
@@ -301,15 +306,18 @@ pub fn find_const_stability(
301
306
) -> Option < ( ConstStability , StabilitySpans ) > {
302
307
let mut level: Option < StabilityLevel > = None ;
303
308
let mut stab_spans = StabilitySpans ( smallvec ! [ ] ) ;
309
+ let mut features = smallvec ! [ ] ;
304
310
let mut promotable = false ;
305
311
306
312
for attr in attrs {
307
313
match attr. name_or_empty ( ) {
308
314
sym:: rustc_promotable => promotable = true ,
309
315
sym:: rustc_const_unstable => {
310
- try_add_unstability ( sess, attr, & mut level, & mut stab_spans)
316
+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features, parse_unstability)
317
+ }
318
+ sym:: rustc_const_stable => {
319
+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features, parse_stability)
311
320
}
312
- sym:: rustc_const_stable => try_add_stability ( sess, attr, & mut level, & mut stab_spans) ,
313
321
_ => { }
314
322
}
315
323
}
@@ -333,76 +341,63 @@ pub fn find_body_stability(
333
341
) -> Option < ( DefaultBodyStability , StabilitySpans ) > {
334
342
let mut level: Option < StabilityLevel > = None ;
335
343
let mut stab_spans = StabilitySpans ( smallvec ! [ ] ) ;
344
+ let mut features = smallvec ! [ ] ;
336
345
337
346
for attr in attrs {
338
347
if attr. has_name ( sym:: rustc_default_body_unstable) {
339
- try_add_unstability ( sess, attr, & mut level, & mut stab_spans) ;
348
+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features , parse_unstability ) ;
340
349
}
341
350
}
342
351
343
352
Some ( ( DefaultBodyStability { level : level? } , stab_spans) )
344
353
}
345
354
346
- /// Collects stability info from one `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
347
- /// attribute, `attr`. Emits an error if the info it collects is inconsistent .
348
- fn try_add_unstability (
355
+ /// Collects stability info from one stability attribute, `attr`.
356
+ /// Emits an error if multiple stability levels are found for the same feature .
357
+ fn add_level (
349
358
sess : & Session ,
350
359
attr : & Attribute ,
351
- level : & mut Option < StabilityLevel > ,
360
+ total_level : & mut Option < StabilityLevel > ,
352
361
stab_spans : & mut StabilitySpans ,
362
+ features : & mut SmallVec < [ Symbol ; 1 ] > ,
363
+ parse_level : impl FnOnce ( & Session , & Attribute ) -> Option < ( Symbol , StabilityLevel ) > ,
353
364
) {
354
365
use StabilityLevel :: * ;
355
366
356
- match level {
357
- // adding #[unstable] to an item with #[stable] is not permitted
358
- Some ( Stable { .. } ) => {
359
- sess. dcx ( ) . emit_err ( session_diagnostics:: MultipleStabilityLevels { span : attr. span } ) ;
360
- }
361
- // if other unstable attributes have been found, attempt to merge them
362
- Some ( Unstable { unstables, is_soft } )
363
- if let Some ( Unstable { unstables : new_unstable, is_soft : new_soft } ) =
364
- parse_unstability ( sess, attr) =>
365
- {
366
- // sanity check: is this the only unstable attr of its kind for its feature?
367
- // FIXME(dianne): should this have a new error associated with it or is "multiple
368
- // stability levels" clear enough, given an update to E0544.md?
369
- // should MultipleStabilityLevels have more fields for diagnostics?
370
- if unstables. iter ( ) . any ( |u| new_unstable. iter ( ) . any ( |v| u. feature == v. feature ) ) {
371
- sess. dcx ( )
372
- . emit_err ( session_diagnostics:: MultipleStabilityLevels { span : attr. span } ) ;
373
- return ;
374
- }
375
- unstables. extend ( new_unstable. clone ( ) ) ;
367
+ let Some ( ( feature, feature_level) ) = parse_level ( sess, attr) else { return } ;
368
+
369
+ // sanity check: is this the only stability level of its kind for its feature?
370
+ if features. contains ( & feature) {
371
+ sess. dcx ( )
372
+ . emit_err ( session_diagnostics:: MultipleStabilityLevels { feature, span : attr. span } ) ;
373
+ }
374
+ features. push ( feature) ;
375
+ stab_spans. 0 . push ( ( feature_level. clone ( ) , attr. span ) ) ;
376
+
377
+ match ( total_level, feature_level) {
378
+ ( level @ None , new_level) => * level = Some ( new_level) ,
379
+ // if multiple unstable attributes have been found, merge them
380
+ (
381
+ Some ( Unstable { unstables, is_soft } ) ,
382
+ Unstable { unstables : new_unstable, is_soft : new_soft } ,
383
+ ) => {
384
+ unstables. extend ( new_unstable) ;
376
385
// Make the unstability soft if any unstable attributes are marked 'soft'; if an
377
386
// unstable item is allowed in stable rust, another attribute shouldn't break that.
378
387
// FIXME(dianne): should there be a check that all unstables are soft if any are?
379
388
* is_soft |= new_soft;
380
- stab_spans. 0 . push ( ( Unstable { unstables : new_unstable, is_soft : new_soft } , attr. span ) ) ;
381
389
}
382
- // if this is the first unstability of its kind on an item, collect it
383
- None if let Some ( new_level) = parse_unstability ( sess, attr) => {
384
- * level = Some ( new_level. clone ( ) ) ;
385
- stab_spans. 0 . push ( ( new_level, attr. span ) ) ;
390
+ // an item with some stable and some unstable features is unstable
391
+ ( Some ( Unstable { .. } ) , Stable { .. } ) => { }
392
+ ( Some ( level @ Stable { .. } ) , new_level @ Unstable { .. } ) => * level = new_level,
393
+ // if multiple stable attributes have been found, use the most recent stabilization date
394
+ (
395
+ Some ( Stable { since, allowed_through_unstable_modules } ) ,
396
+ Stable { since : new_since, allowed_through_unstable_modules : new_allowed } ,
397
+ ) => {
398
+ * since = StableSince :: max ( * since, new_since) ;
399
+ * allowed_through_unstable_modules |= new_allowed;
386
400
}
387
- // if there was an error in `parse_unstability`, it's already been emitted; do nothing
388
- _ => { }
389
- }
390
- }
391
-
392
- /// Collects stability info from a single `stable`/`rustc_const_stable` attribute, `attr`.
393
- /// Emits an error if the info it collects is inconsistent.
394
- fn try_add_stability (
395
- sess : & Session ,
396
- attr : & Attribute ,
397
- level : & mut Option < StabilityLevel > ,
398
- stab_spans : & mut StabilitySpans ,
399
- ) {
400
- // at most one #[stable] attribute is permitted, and not when #[unstable] is present
401
- if level. is_some ( ) {
402
- sess. dcx ( ) . emit_err ( session_diagnostics:: MultipleStabilityLevels { span : attr. span } ) ;
403
- } else if let Some ( new_level) = parse_stability ( sess, attr) {
404
- * level = Some ( new_level. clone ( ) ) ;
405
- stab_spans. 0 . push ( ( new_level, attr. span ) ) ;
406
401
}
407
402
}
408
403
@@ -424,7 +419,7 @@ fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option<Symbol>) -
424
419
425
420
/// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and
426
421
/// its stability information.
427
- fn parse_stability ( sess : & Session , attr : & Attribute ) -> Option < StabilityLevel > {
422
+ fn parse_stability ( sess : & Session , attr : & Attribute ) -> Option < ( Symbol , StabilityLevel ) > {
428
423
let meta = attr. meta ( ) ?;
429
424
let MetaItem { kind : MetaItemKind :: List ( ref metas) , .. } = meta else { return None } ;
430
425
@@ -478,16 +473,17 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<StabilityLevel> {
478
473
} ;
479
474
480
475
match feature {
481
- Ok ( _feature) => {
482
- Some ( StabilityLevel :: Stable { since, allowed_through_unstable_modules : false } )
483
- }
476
+ Ok ( feature) => Some ( ( feature, StabilityLevel :: Stable {
477
+ since,
478
+ allowed_through_unstable_modules : false ,
479
+ } ) ) ,
484
480
Err ( ErrorGuaranteed { .. } ) => None ,
485
481
}
486
482
}
487
483
488
484
/// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
489
485
/// attribute, and return the feature name and its stability information.
490
- fn parse_unstability ( sess : & Session , attr : & Attribute ) -> Option < StabilityLevel > {
486
+ fn parse_unstability ( sess : & Session , attr : & Attribute ) -> Option < ( Symbol , StabilityLevel ) > {
491
487
let meta = attr. meta ( ) ?;
492
488
let MetaItem { kind : MetaItemKind :: List ( ref metas) , .. } = meta else { return None } ;
493
489
@@ -572,7 +568,7 @@ fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<StabilityLevel>
572
568
issue : issue_num,
573
569
implied_by,
574
570
} ;
575
- Some ( StabilityLevel :: Unstable { unstables : smallvec ! [ unstability] , is_soft } )
571
+ Some ( ( feature , StabilityLevel :: Unstable { unstables : smallvec ! [ unstability] , is_soft } ) )
576
572
}
577
573
( Err ( ErrorGuaranteed { .. } ) , _) | ( _, Err ( ErrorGuaranteed { .. } ) ) => None ,
578
574
}
0 commit comments