@@ -18,8 +18,8 @@ use rustc_hir::{
1818} ;
1919use rustc_lint_defs as lint;
2020use rustc_lint_defs:: builtin:: {
21- DEPRECATED , DUPLICATE_FEATURES , INEFFECTIVE_UNSTABLE_TRAIT_IMPL , STABLE_FEATURES ,
22- UNUSED_UNSTABLE_REEXPORT_ATTRIBUTES ,
21+ DEPRECATED , DUPLICATE_FEATURES , INCOMPATIBLE_REEXPORT_STABILITY ,
22+ INEFFECTIVE_UNSTABLE_TRAIT_IMPL , STABLE_FEATURES ,
2323} ;
2424use rustc_middle:: hir:: nested_filter;
2525use rustc_middle:: metadata:: Reexport ;
@@ -525,9 +525,9 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {
525525/// Cross-references the feature names of unstable APIs with enabled
526526/// features and possibly prints errors.
527527fn check_mod_unstable_api_usage ( tcx : TyCtxt < ' _ > , mod_id : LocalModId ) {
528- let mut checker = Checker { tcx, mod_id, unstable_reexports : FxIndexMap :: default ( ) } ;
528+ let mut checker = Checker { tcx, mod_id, reexport_stability : FxIndexMap :: default ( ) } ;
529529 tcx. hir_visit_item_likes_in_module ( mod_id, & mut checker) ;
530- checker. emit_unused_unstable_reexport_attributes ( ) ;
530+ checker. emit_incompatible_reexport_stability ( ) ;
531531
532532 let is_staged_api =
533533 tcx. sess . opts . unstable_opts . force_unstable_if_unmarked || tcx. features ( ) . staged_api ( ) ;
@@ -557,99 +557,140 @@ pub(crate) fn provide(providers: &mut Providers) {
557557 } ;
558558}
559559
560- struct UnstableReexport {
560+ struct ReexportStability {
561561 hir_id : HirId ,
562562 span : Span ,
563563 has_target : bool ,
564- all_targets_stable : bool ,
564+ all_targets_compatible : bool ,
565565}
566566
567567struct Checker < ' tcx > {
568568 tcx : TyCtxt < ' tcx > ,
569569 mod_id : LocalModId ,
570- unstable_reexports : FxIndexMap < Span , UnstableReexport > ,
570+ reexport_stability : FxIndexMap < Span , ReexportStability > ,
571571}
572572
573573impl < ' tcx > Checker < ' tcx > {
574- fn unstable_reexport_span ( & self , item : & ' tcx hir:: Item < ' tcx > ) -> Option < Span > {
574+ fn reexport_stability_attr ( & self , item : & ' tcx hir:: Item < ' tcx > ) -> Option < ( Stability , Span ) > {
575575 let attrs = self . tcx . hir_attrs ( item. hir_id ( ) ) ;
576- let ( stability, span) =
577- find_attr ! ( attrs, Stability { stability, span } => ( * stability, * span) ) ?;
576+ find_attr ! ( attrs, Stability { stability, span } => ( * stability, * span) )
577+ }
578+
579+ fn stability_is_compatible ( reexport : & Stability , target : & Stability ) -> bool {
580+ match ( & reexport. level , & target. level ) {
581+ (
582+ StabilityLevel :: Stable { since : reexport_since, .. } ,
583+ StabilityLevel :: Stable { since : target_since, .. } ,
584+ ) => {
585+ // Avoid another error for an invalid `since`.
586+ matches ! (
587+ ( * reexport_since, * target_since) ,
588+ ( StableSince :: Err ( _) , _) | ( _, StableSince :: Err ( _) )
589+ ) || ( reexport. feature == target. feature && reexport_since == target_since)
590+ }
591+
592+ (
593+ StabilityLevel :: Unstable { issue : reexport_issue, .. } ,
594+ StabilityLevel :: Unstable { issue : target_issue, .. } ,
595+ ) => reexport. feature == target. feature && reexport_issue == target_issue,
578596
579- matches ! ( stability. level, StabilityLevel :: Unstable { .. } ) . then_some ( span)
597+ // An unstable re-export cannot make a stable item unstable.
598+ ( StabilityLevel :: Unstable { .. } , StabilityLevel :: Stable { .. } ) => false ,
599+
600+ // Stable re-exports of unstable items are handled elsewhere.
601+ ( StabilityLevel :: Stable { .. } , StabilityLevel :: Unstable { .. } ) => true ,
602+ }
580603 }
581604
582605 fn classify_reexport_targets < Id > (
583606 & self ,
607+ own_stability : & Stability ,
584608 targets : impl IntoIterator < Item = Res < Id > > ,
585609 ) -> ( bool , bool ) {
586610 let mut has_target = false ;
587- let mut all_targets_stable = true ;
611+ let mut all_targets_compatible = true ;
588612
589613 for res in targets {
590614 match res {
591615 Res :: Def ( _, def_id) => {
592616 has_target = true ;
593617
594- if self . tcx . lookup_stability ( def_id) . is_some_and ( |stab| !stab. level . is_stable ( ) )
618+ if let Some ( target_stability) = self . tcx . lookup_stability ( def_id)
619+ && !Self :: stability_is_compatible ( own_stability, & target_stability)
595620 {
596- all_targets_stable = false ;
621+ all_targets_compatible = false ;
597622 }
598623 }
599624
600625 Res :: PrimTy ( _) => {
601626 has_target = true ;
602- }
603627
604- _ => {
605- all_targets_stable = false ;
628+ // Primitives are stable and have no DefId.
629+ if own_stability. level . is_unstable ( ) {
630+ all_targets_compatible = false ;
631+ }
606632 }
633+
634+ // No stability metadata to compare.
635+ _ => { }
607636 }
608637 }
609638
610- ( has_target, all_targets_stable )
639+ ( has_target, all_targets_compatible )
611640 }
612641
613- fn record_unstable_reexport (
642+ fn record_reexport_stability (
614643 & mut self ,
615644 item : & ' tcx hir:: Item < ' tcx > ,
616645 attr_span : Span ,
617646 span : Span ,
618647 has_target : bool ,
619- all_targets_stable : bool ,
648+ all_targets_compatible : bool ,
620649 ) {
621- let entry = self . unstable_reexports . entry ( attr_span) . or_insert ( UnstableReexport {
650+ let entry = self . reexport_stability . entry ( attr_span) . or_insert ( ReexportStability {
622651 hir_id : item. hir_id ( ) ,
623652 span,
624653 has_target : false ,
625- all_targets_stable : true ,
654+ all_targets_compatible : true ,
626655 } ) ;
627656
628657 entry. has_target |= has_target;
629- entry. all_targets_stable &= all_targets_stable;
658+
659+ // Keep the first bad path for the diagnostic.
660+ if entry. all_targets_compatible && !all_targets_compatible {
661+ entry. span = span;
662+ }
663+
664+ entry. all_targets_compatible &= all_targets_compatible;
630665 }
631666
632- fn check_single_unstable_reexport (
667+ fn check_single_reexport_stability (
633668 & mut self ,
634669 item : & ' tcx hir:: Item < ' tcx > ,
635670 path : & ' tcx UsePath < ' tcx > ,
636671 ) {
637- let Some ( attr_span) = self . unstable_reexport_span ( item) else {
672+ let Some ( ( own_stability , attr_span) ) = self . reexport_stability_attr ( item) else {
638673 return ;
639674 } ;
640675
641- let ( has_target, all_targets_stable ) =
642- self . classify_reexport_targets ( path. res . present_items ( ) ) ;
676+ let ( has_target, all_targets_compatible ) =
677+ self . classify_reexport_targets ( & own_stability , path. res . present_items ( ) ) ;
643678
644- self . record_unstable_reexport ( item, attr_span, path. span , has_target, all_targets_stable) ;
679+ self . record_reexport_stability (
680+ item,
681+ attr_span,
682+ path. span ,
683+ has_target,
684+ all_targets_compatible,
685+ ) ;
645686 }
646687
647- fn check_glob_unstable_reexport (
688+ fn check_glob_reexport_stability (
648689 & mut self ,
649690 item : & ' tcx hir:: Item < ' tcx > ,
650691 path : & ' tcx UsePath < ' tcx > ,
651692 ) {
652- let Some ( attr_span) = self . unstable_reexport_span ( item) else {
693+ let Some ( ( own_stability , attr_span) ) = self . reexport_stability_attr ( item) else {
653694 return ;
654695 } ;
655696
@@ -669,19 +710,26 @@ impl<'tcx> Checker<'tcx> {
669710 } )
670711 . map ( |child| child. res ) ;
671712
672- let ( has_target, all_targets_stable) = self . classify_reexport_targets ( targets) ;
713+ let ( has_target, all_targets_compatible) =
714+ self . classify_reexport_targets ( & own_stability, targets) ;
673715
674- self . record_unstable_reexport ( item, attr_span, path. span , has_target, all_targets_stable) ;
716+ self . record_reexport_stability (
717+ item,
718+ attr_span,
719+ path. span ,
720+ has_target,
721+ all_targets_compatible,
722+ ) ;
675723 }
676724
677- fn emit_unused_unstable_reexport_attributes ( & self ) {
678- for reexport in self . unstable_reexports . values ( ) {
679- if reexport. has_target && reexport. all_targets_stable {
725+ fn emit_incompatible_reexport_stability ( & self ) {
726+ for reexport in self . reexport_stability . values ( ) {
727+ if reexport. has_target && ! reexport. all_targets_compatible {
680728 self . tcx . emit_node_span_lint (
681- UNUSED_UNSTABLE_REEXPORT_ATTRIBUTES ,
729+ INCOMPATIBLE_REEXPORT_STABILITY ,
682730 reexport. hir_id ,
683731 reexport. span ,
684- diagnostics:: UnusedUnstableReexportAttributes ,
732+ diagnostics:: IncompatibleReexportStability ,
685733 ) ;
686734 }
687735 }
@@ -718,14 +766,14 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
718766 if self . tcx . features ( ) . staged_api ( )
719767 && self . tcx . local_visibility ( item. owner_id . def_id ) . is_public ( ) =>
720768 {
721- self . check_single_unstable_reexport ( item, path) ;
769+ self . check_single_reexport_stability ( item, path) ;
722770 }
723771
724772 hir:: ItemKind :: Use ( path, hir:: UseKind :: Glob )
725773 if self . tcx . features ( ) . staged_api ( )
726774 && self . tcx . local_visibility ( item. owner_id . def_id ) . is_public ( ) =>
727775 {
728- self . check_glob_unstable_reexport ( item, path) ;
776+ self . check_glob_reexport_stability ( item, path) ;
729777 }
730778
731779 // For implementations of traits, check the stability of each item
0 commit comments