@@ -962,58 +962,113 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
962962 remaining_lib_features. remove ( & sym:: libc) ;
963963 remaining_lib_features. remove ( & sym:: test) ;
964964
965- // We always collect the lib features declared in the current crate, even if there are
966- // no unknown features, because the collection also does feature attribute validation.
967- let local_defined_features = tcx. lib_features ( ( ) ) ;
968- let mut all_lib_features: FxHashMap < _ , _ > =
969- local_defined_features. to_vec ( ) . iter ( ) . map ( |el| * el) . collect ( ) ;
970- let mut implications = tcx. stability_implications ( rustc_hir:: def_id:: LOCAL_CRATE ) . clone ( ) ;
971- for & cnum in tcx. crates ( ( ) ) {
972- implications. extend ( tcx. stability_implications ( cnum) ) ;
973- all_lib_features. extend ( tcx. defined_lib_features ( cnum) . iter ( ) . map ( |el| * el) ) ;
974- }
975-
976- // Check that every feature referenced by an `implied_by` exists (for features defined in the
977- // local crate).
978- for ( implied_by, feature) in tcx. stability_implications ( rustc_hir:: def_id:: LOCAL_CRATE ) {
979- // Only `implied_by` needs to be checked, `feature` is guaranteed to exist.
980- if !all_lib_features. contains_key ( implied_by) {
981- let span = local_defined_features
982- . stable
983- . get ( feature)
984- . map ( |( _, span) | span)
985- . or_else ( || local_defined_features. unstable . get ( feature) )
986- . expect ( "feature that implied another does not exist" ) ;
987- tcx. sess
988- . struct_span_err (
989- * span,
990- format ! ( "feature `{implied_by}` implying `{feature}` does not exist" ) ,
991- )
992- . emit ( ) ;
993- }
994- }
995-
996- if !remaining_lib_features. is_empty ( ) {
997- for ( feature, since) in all_lib_features. iter ( ) {
965+ /// For each feature in `defined_features`..
966+ ///
967+ /// - If it is in `remaining_lib_features` (those features with `#![feature(..)]` attributes in
968+ /// the current crate), check if it is stable (or partially stable) and thus an unnecessary
969+ /// attribute.
970+ /// - If it is in `remaining_implications` (a feature that is referenced by an `implied_by`
971+ /// from the current crate), then remove it from the remaining implications.
972+ ///
973+ /// Once this function has been invoked for every feature (local crate and all extern crates),
974+ /// then..
975+ ///
976+ /// - If features remain in `remaining_lib_features`, then the user has enabled a feature that
977+ /// does not exist.
978+ /// - If features remain in `remaining_implications`, the `implied_by` refers to a feature that
979+ /// does not exist.
980+ ///
981+ /// By structuring the code in this way: checking the features defined from each crate one at a
982+ /// time, less loading from metadata is performed and thus compiler performance is improved.
983+ fn check_features < ' tcx > (
984+ tcx : TyCtxt < ' tcx > ,
985+ remaining_lib_features : & mut FxIndexMap < & Symbol , Span > ,
986+ remaining_implications : & mut FxHashMap < Symbol , Symbol > ,
987+ defined_features : & [ ( Symbol , Option < Symbol > ) ] ,
988+ all_implications : & FxHashMap < Symbol , Symbol > ,
989+ ) {
990+ for ( feature, since) in defined_features {
998991 if let Some ( since) = since && let Some ( span) = remaining_lib_features. get ( & feature) {
999992 // Warn if the user has enabled an already-stable lib feature.
1000- if let Some ( implies) = implications . get ( & feature) {
993+ if let Some ( implies) = all_implications . get ( & feature) {
1001994 unnecessary_partially_stable_feature_lint ( tcx, * span, * feature, * implies, * since) ;
1002995 } else {
1003996 unnecessary_stable_feature_lint ( tcx, * span, * feature, * since) ;
1004997 }
998+
1005999 }
1006- remaining_lib_features. remove ( & feature) ;
1007- if remaining_lib_features. is_empty ( ) {
1000+ remaining_lib_features. remove ( feature) ;
1001+
1002+ // `feature` is the feature doing the implying, but `implied_by` is the feature with
1003+ // the attribute that establishes this relationship. `implied_by` is guaranteed to be a
1004+ // feature defined in the local crate because `remaining_implications` is only the
1005+ // implications from this crate.
1006+ remaining_implications. remove ( feature) ;
1007+
1008+ if remaining_lib_features. is_empty ( ) && remaining_implications. is_empty ( ) {
10081009 break ;
10091010 }
10101011 }
10111012 }
10121013
1014+ // All local crate implications need to have the feature that implies it confirmed to exist.
1015+ let mut remaining_implications =
1016+ tcx. stability_implications ( rustc_hir:: def_id:: LOCAL_CRATE ) . clone ( ) ;
1017+
1018+ // We always collect the lib features declared in the current crate, even if there are
1019+ // no unknown features, because the collection also does feature attribute validation.
1020+ let local_defined_features = tcx. lib_features ( ( ) ) . to_vec ( ) ;
1021+ if !remaining_lib_features. is_empty ( ) || !remaining_implications. is_empty ( ) {
1022+ // Loading the implications of all crates is unavoidable to be able to emit the partial
1023+ // stabilization diagnostic, but it can be avoided when there are no
1024+ // `remaining_lib_features`.
1025+ let mut all_implications = remaining_implications. clone ( ) ;
1026+ for & cnum in tcx. crates ( ( ) ) {
1027+ all_implications. extend ( tcx. stability_implications ( cnum) ) ;
1028+ }
1029+
1030+ check_features (
1031+ tcx,
1032+ & mut remaining_lib_features,
1033+ & mut remaining_implications,
1034+ local_defined_features. as_slice ( ) ,
1035+ & all_implications,
1036+ ) ;
1037+
1038+ for & cnum in tcx. crates ( ( ) ) {
1039+ if remaining_lib_features. is_empty ( ) && remaining_implications. is_empty ( ) {
1040+ break ;
1041+ }
1042+ check_features (
1043+ tcx,
1044+ & mut remaining_lib_features,
1045+ & mut remaining_implications,
1046+ tcx. defined_lib_features ( cnum) . to_vec ( ) . as_slice ( ) ,
1047+ & all_implications,
1048+ ) ;
1049+ }
1050+ }
1051+
10131052 for ( feature, span) in remaining_lib_features {
10141053 struct_span_err ! ( tcx. sess, span, E0635 , "unknown feature `{}`" , feature) . emit ( ) ;
10151054 }
10161055
1056+ for ( implied_by, feature) in remaining_implications {
1057+ let local_defined_features = tcx. lib_features ( ( ) ) ;
1058+ let span = local_defined_features
1059+ . stable
1060+ . get ( & feature)
1061+ . map ( |( _, span) | span)
1062+ . or_else ( || local_defined_features. unstable . get ( & feature) )
1063+ . expect ( "feature that implied another does not exist" ) ;
1064+ tcx. sess
1065+ . struct_span_err (
1066+ * span,
1067+ format ! ( "feature `{implied_by}` implying `{feature}` does not exist" ) ,
1068+ )
1069+ . emit ( ) ;
1070+ }
1071+
10171072 // FIXME(#44232): the `used_features` table no longer exists, so we
10181073 // don't lint about unused features. We should re-enable this one day!
10191074}
0 commit comments