@@ -949,58 +949,113 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
949949 remaining_lib_features. remove ( & sym:: libc) ;
950950 remaining_lib_features. remove ( & sym:: test) ;
951951
952- // We always collect the lib features declared in the current crate, even if there are
953- // no unknown features, because the collection also does feature attribute validation.
954- let local_defined_features = tcx. lib_features ( ( ) ) ;
955- let mut all_lib_features: FxHashMap < _ , _ > =
956- local_defined_features. to_vec ( ) . iter ( ) . map ( |el| * el) . collect ( ) ;
957- let mut implications = tcx. stability_implications ( rustc_hir:: def_id:: LOCAL_CRATE ) . clone ( ) ;
958- for & cnum in tcx. crates ( ( ) ) {
959- implications. extend ( tcx. stability_implications ( cnum) ) ;
960- all_lib_features. extend ( tcx. defined_lib_features ( cnum) . iter ( ) . map ( |el| * el) ) ;
961- }
962-
963- // Check that every feature referenced by an `implied_by` exists (for features defined in the
964- // local crate).
965- for ( implied_by, feature) in tcx. stability_implications ( rustc_hir:: def_id:: LOCAL_CRATE ) {
966- // Only `implied_by` needs to be checked, `feature` is guaranteed to exist.
967- if !all_lib_features. contains_key ( implied_by) {
968- let span = local_defined_features
969- . stable
970- . get ( feature)
971- . map ( |( _, span) | span)
972- . or_else ( || local_defined_features. unstable . get ( feature) )
973- . expect ( "feature that implied another does not exist" ) ;
974- tcx. sess
975- . struct_span_err (
976- * span,
977- format ! ( "feature `{implied_by}` implying `{feature}` does not exist" ) ,
978- )
979- . emit ( ) ;
980- }
981- }
982-
983- if !remaining_lib_features. is_empty ( ) {
984- for ( feature, since) in all_lib_features. iter ( ) {
952+ /// For each feature in `defined_features`..
953+ ///
954+ /// - If it is in `remaining_lib_features` (those features with `#![feature(..)]` attributes in
955+ /// the current crate), check if it is stable (or partially stable) and thus an unnecessary
956+ /// attribute.
957+ /// - If it is in `remaining_implications` (a feature that is referenced by an `implied_by`
958+ /// from the current crate), then remove it from the remaining implications.
959+ ///
960+ /// Once this function has been invoked for every feature (local crate and all extern crates),
961+ /// then..
962+ ///
963+ /// - If features remain in `remaining_lib_features`, then the user has enabled a feature that
964+ /// does not exist.
965+ /// - If features remain in `remaining_implications`, the `implied_by` refers to a feature that
966+ /// does not exist.
967+ ///
968+ /// By structuring the code in this way: checking the features defined from each crate one at a
969+ /// time, less loading from metadata is performed and thus compiler performance is improved.
970+ fn check_features < ' tcx > (
971+ tcx : TyCtxt < ' tcx > ,
972+ remaining_lib_features : & mut FxIndexMap < & Symbol , Span > ,
973+ remaining_implications : & mut FxHashMap < Symbol , Symbol > ,
974+ defined_features : & [ ( Symbol , Option < Symbol > ) ] ,
975+ all_implications : & FxHashMap < Symbol , Symbol > ,
976+ ) {
977+ for ( feature, since) in defined_features {
985978 if let Some ( since) = since && let Some ( span) = remaining_lib_features. get ( & feature) {
986979 // Warn if the user has enabled an already-stable lib feature.
987- if let Some ( implies) = implications . get ( & feature) {
980+ if let Some ( implies) = all_implications . get ( & feature) {
988981 unnecessary_partially_stable_feature_lint ( tcx, * span, * feature, * implies, * since) ;
989982 } else {
990983 unnecessary_stable_feature_lint ( tcx, * span, * feature, * since) ;
991984 }
985+
992986 }
993- remaining_lib_features. remove ( & feature) ;
994- if remaining_lib_features. is_empty ( ) {
987+ remaining_lib_features. remove ( feature) ;
988+
989+ // `feature` is the feature doing the implying, but `implied_by` is the feature with
990+ // the attribute that establishes this relationship. `implied_by` is guaranteed to be a
991+ // feature defined in the local crate because `remaining_implications` is only the
992+ // implications from this crate.
993+ remaining_implications. remove ( feature) ;
994+
995+ if remaining_lib_features. is_empty ( ) && remaining_implications. is_empty ( ) {
995996 break ;
996997 }
997998 }
998999 }
9991000
1001+ // All local crate implications need to have the feature that implies it confirmed to exist.
1002+ let mut remaining_implications =
1003+ tcx. stability_implications ( rustc_hir:: def_id:: LOCAL_CRATE ) . clone ( ) ;
1004+
1005+ // We always collect the lib features declared in the current crate, even if there are
1006+ // no unknown features, because the collection also does feature attribute validation.
1007+ let local_defined_features = tcx. lib_features ( ( ) ) . to_vec ( ) ;
1008+ if !remaining_lib_features. is_empty ( ) || !remaining_implications. is_empty ( ) {
1009+ // Loading the implications of all crates is unavoidable to be able to emit the partial
1010+ // stabilization diagnostic, but it can be avoided when there are no
1011+ // `remaining_lib_features`.
1012+ let mut all_implications = remaining_implications. clone ( ) ;
1013+ for & cnum in tcx. crates ( ( ) ) {
1014+ all_implications. extend ( tcx. stability_implications ( cnum) ) ;
1015+ }
1016+
1017+ check_features (
1018+ tcx,
1019+ & mut remaining_lib_features,
1020+ & mut remaining_implications,
1021+ local_defined_features. as_slice ( ) ,
1022+ & all_implications,
1023+ ) ;
1024+
1025+ for & cnum in tcx. crates ( ( ) ) {
1026+ if remaining_lib_features. is_empty ( ) && remaining_implications. is_empty ( ) {
1027+ break ;
1028+ }
1029+ check_features (
1030+ tcx,
1031+ & mut remaining_lib_features,
1032+ & mut remaining_implications,
1033+ tcx. defined_lib_features ( cnum) . to_vec ( ) . as_slice ( ) ,
1034+ & all_implications,
1035+ ) ;
1036+ }
1037+ }
1038+
10001039 for ( feature, span) in remaining_lib_features {
10011040 struct_span_err ! ( tcx. sess, span, E0635 , "unknown feature `{}`" , feature) . emit ( ) ;
10021041 }
10031042
1043+ for ( implied_by, feature) in remaining_implications {
1044+ let local_defined_features = tcx. lib_features ( ( ) ) ;
1045+ let span = local_defined_features
1046+ . stable
1047+ . get ( & feature)
1048+ . map ( |( _, span) | span)
1049+ . or_else ( || local_defined_features. unstable . get ( & feature) )
1050+ . expect ( "feature that implied another does not exist" ) ;
1051+ tcx. sess
1052+ . struct_span_err (
1053+ * span,
1054+ format ! ( "feature `{implied_by}` implying `{feature}` does not exist" ) ,
1055+ )
1056+ . emit ( ) ;
1057+ }
1058+
10041059 // FIXME(#44232): the `used_features` table no longer exists, so we
10051060 // don't lint about unused features. We should re-enable this one day!
10061061}
0 commit comments