@@ -336,13 +336,18 @@ pub fn find_stability(
336
336
) -> Option < ( Stability , StabilitySpans ) > {
337
337
let mut level: Option < StabilityLevel > = None ;
338
338
let mut stab_spans = StabilitySpans ( smallvec ! [ ] ) ;
339
+ let mut features = smallvec ! [ ] ;
339
340
let mut allowed_through_unstable_modules = false ;
340
341
341
342
for attr in attrs {
342
343
match attr. name_or_empty ( ) {
343
344
sym:: rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true ,
344
- sym:: unstable => try_add_unstability ( sess, attr, & mut level, & mut stab_spans) ,
345
- sym:: stable => try_add_stability ( sess, attr, & mut level, & mut stab_spans) ,
345
+ sym:: unstable => {
346
+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features, parse_unstability)
347
+ }
348
+ sym:: stable => {
349
+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features, parse_stability)
350
+ }
346
351
_ => { }
347
352
}
348
353
}
@@ -375,6 +380,7 @@ pub fn find_const_stability(
375
380
) -> Option < ( ConstStability , ConstStabilitySpans ) > {
376
381
let mut level: Option < StabilityLevel > = None ;
377
382
let mut stab_spans = StabilitySpans ( smallvec ! [ ] ) ;
383
+ let mut features = smallvec ! [ ] ;
378
384
let mut promotable = false ;
379
385
let mut const_stable_indirect = None ;
380
386
@@ -383,9 +389,11 @@ pub fn find_const_stability(
383
389
sym:: rustc_promotable => promotable = true ,
384
390
sym:: rustc_const_stable_indirect => const_stable_indirect = Some ( attr. span ) ,
385
391
sym:: rustc_const_unstable => {
386
- try_add_unstability ( sess, attr, & mut level, & mut stab_spans)
392
+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features, parse_unstability)
393
+ }
394
+ sym:: rustc_const_stable => {
395
+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features, parse_stability)
387
396
}
388
- sym:: rustc_const_stable => try_add_stability ( sess, attr, & mut level, & mut stab_spans) ,
389
397
_ => { }
390
398
}
391
399
}
@@ -441,76 +449,63 @@ pub fn find_body_stability(
441
449
) -> Option < ( DefaultBodyStability , StabilitySpans ) > {
442
450
let mut level: Option < StabilityLevel > = None ;
443
451
let mut stab_spans = StabilitySpans ( smallvec ! [ ] ) ;
452
+ let mut features = smallvec ! [ ] ;
444
453
445
454
for attr in attrs {
446
455
if attr. has_name ( sym:: rustc_default_body_unstable) {
447
- try_add_unstability ( sess, attr, & mut level, & mut stab_spans) ;
456
+ add_level ( sess, attr, & mut level, & mut stab_spans, & mut features , parse_unstability ) ;
448
457
}
449
458
}
450
459
451
460
Some ( ( DefaultBodyStability { level : level? } , stab_spans) )
452
461
}
453
462
454
- /// Collects stability info from one `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
455
- /// attribute, `attr`. Emits an error if the info it collects is inconsistent .
456
- fn try_add_unstability (
463
+ /// Collects stability info from one stability attribute, `attr`.
464
+ /// Emits an error if multiple stability levels are found for the same feature .
465
+ fn add_level (
457
466
sess : & Session ,
458
467
attr : & Attribute ,
459
- level : & mut Option < StabilityLevel > ,
468
+ total_level : & mut Option < StabilityLevel > ,
460
469
stab_spans : & mut StabilitySpans ,
470
+ features : & mut SmallVec < [ Symbol ; 1 ] > ,
471
+ parse_level : impl FnOnce ( & Session , & Attribute ) -> Option < ( Symbol , StabilityLevel ) > ,
461
472
) {
462
473
use StabilityLevel :: * ;
463
474
464
- match level {
465
- // adding #[unstable] to an item with #[stable] is not permitted
466
- Some ( Stable { .. } ) => {
467
- sess. dcx ( ) . emit_err ( session_diagnostics:: MultipleStabilityLevels { span : attr. span } ) ;
468
- }
469
- // if other unstable attributes have been found, attempt to merge them
470
- Some ( Unstable { unstables, is_soft } )
471
- if let Some ( Unstable { unstables : new_unstable, is_soft : new_soft } ) =
472
- parse_unstability ( sess, attr) =>
473
- {
474
- // sanity check: is this the only unstable attr of its kind for its feature?
475
- // FIXME(dianne): should this have a new error associated with it or is "multiple
476
- // stability levels" clear enough, given an update to E0544.md?
477
- // should MultipleStabilityLevels have more fields for diagnostics?
478
- if unstables. iter ( ) . any ( |u| new_unstable. iter ( ) . any ( |v| u. feature == v. feature ) ) {
479
- sess. dcx ( )
480
- . emit_err ( session_diagnostics:: MultipleStabilityLevels { span : attr. span } ) ;
481
- return ;
482
- }
483
- unstables. extend ( new_unstable. clone ( ) ) ;
475
+ let Some ( ( feature, feature_level) ) = parse_level ( sess, attr) else { return } ;
476
+
477
+ // sanity check: is this the only stability level of its kind for its feature?
478
+ if features. contains ( & feature) {
479
+ sess. dcx ( )
480
+ . emit_err ( session_diagnostics:: MultipleStabilityLevels { feature, span : attr. span } ) ;
481
+ }
482
+ features. push ( feature) ;
483
+ stab_spans. 0 . push ( ( feature_level. clone ( ) , attr. span ) ) ;
484
+
485
+ match ( total_level, feature_level) {
486
+ ( level @ None , new_level) => * level = Some ( new_level) ,
487
+ // if multiple unstable attributes have been found, merge them
488
+ (
489
+ Some ( Unstable { unstables, is_soft } ) ,
490
+ Unstable { unstables : new_unstable, is_soft : new_soft } ,
491
+ ) => {
492
+ unstables. extend ( new_unstable) ;
484
493
// Make the unstability soft if any unstable attributes are marked 'soft'; if an
485
494
// unstable item is allowed in stable rust, another attribute shouldn't break that.
486
495
// FIXME(dianne): should there be a check that all unstables are soft if any are?
487
496
* is_soft |= new_soft;
488
- stab_spans. 0 . push ( ( Unstable { unstables : new_unstable, is_soft : new_soft } , attr. span ) ) ;
489
497
}
490
- // if this is the first unstability of its kind on an item, collect it
491
- None if let Some ( new_level) = parse_unstability ( sess, attr) => {
492
- * level = Some ( new_level. clone ( ) ) ;
493
- stab_spans. 0 . push ( ( new_level, attr. span ) ) ;
498
+ // an item with some stable and some unstable features is unstable
499
+ ( Some ( Unstable { .. } ) , Stable { .. } ) => { }
500
+ ( Some ( level @ Stable { .. } ) , new_level @ Unstable { .. } ) => * level = new_level,
501
+ // if multiple stable attributes have been found, use the most recent stabilization date
502
+ (
503
+ Some ( Stable { since, allowed_through_unstable_modules } ) ,
504
+ Stable { since : new_since, allowed_through_unstable_modules : new_allowed } ,
505
+ ) => {
506
+ * since = StableSince :: max ( * since, new_since) ;
507
+ * allowed_through_unstable_modules |= new_allowed;
494
508
}
495
- // if there was an error in `parse_unstability`, it's already been emitted; do nothing
496
- _ => { }
497
- }
498
- }
499
-
500
- /// Collects stability info from a single `stable`/`rustc_const_stable` attribute, `attr`.
501
- /// Emits an error if the info it collects is inconsistent.
502
- fn try_add_stability (
503
- sess : & Session ,
504
- attr : & Attribute ,
505
- level : & mut Option < StabilityLevel > ,
506
- stab_spans : & mut StabilitySpans ,
507
- ) {
508
- // at most one #[stable] attribute is permitted, and not when #[unstable] is present
509
- if level. is_some ( ) {
510
- sess. dcx ( ) . emit_err ( session_diagnostics:: MultipleStabilityLevels { span : attr. span } ) ;
511
- } else if let Some ( new_level) = parse_stability ( sess, attr) {
512
- * level = Some ( new_level. clone ( ) ) ;
513
- stab_spans. 0 . push ( ( new_level, attr. span ) ) ;
514
509
}
515
510
}
516
511
@@ -532,7 +527,7 @@ fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option<Symbol>) -
532
527
533
528
/// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and
534
529
/// its stability information.
535
- fn parse_stability ( sess : & Session , attr : & Attribute ) -> Option < StabilityLevel > {
530
+ fn parse_stability ( sess : & Session , attr : & Attribute ) -> Option < ( Symbol , StabilityLevel ) > {
536
531
let meta = attr. meta ( ) ?;
537
532
let MetaItem { kind : MetaItemKind :: List ( ref metas) , .. } = meta else { return None } ;
538
533
@@ -586,16 +581,17 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<StabilityLevel> {
586
581
} ;
587
582
588
583
match feature {
589
- Ok ( _feature) => {
590
- Some ( StabilityLevel :: Stable { since, allowed_through_unstable_modules : false } )
591
- }
584
+ Ok ( feature) => Some ( ( feature, StabilityLevel :: Stable {
585
+ since,
586
+ allowed_through_unstable_modules : false ,
587
+ } ) ) ,
592
588
Err ( ErrorGuaranteed { .. } ) => None ,
593
589
}
594
590
}
595
591
596
592
/// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
597
593
/// attribute, and return the feature name and its stability information.
598
- fn parse_unstability ( sess : & Session , attr : & Attribute ) -> Option < StabilityLevel > {
594
+ fn parse_unstability ( sess : & Session , attr : & Attribute ) -> Option < ( Symbol , StabilityLevel ) > {
599
595
let meta = attr. meta ( ) ?;
600
596
let MetaItem { kind : MetaItemKind :: List ( ref metas) , .. } = meta else { return None } ;
601
597
@@ -680,7 +676,7 @@ fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<StabilityLevel>
680
676
issue : issue_num,
681
677
implied_by,
682
678
} ;
683
- Some ( StabilityLevel :: Unstable { unstables : smallvec ! [ unstability] , is_soft } )
679
+ Some ( ( feature , StabilityLevel :: Unstable { unstables : smallvec ! [ unstability] , is_soft } ) )
684
680
}
685
681
( Err ( ErrorGuaranteed { .. } ) , _) | ( _, Err ( ErrorGuaranteed { .. } ) ) => None ,
686
682
}
0 commit comments