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