9292//! of that page. Update the rest of the documentation to add the new
9393//! feature.
9494
95+ use std:: collections:: BTreeSet ;
9596use std:: env;
9697use std:: fmt;
9798use std:: str:: FromStr ;
@@ -101,7 +102,7 @@ use cargo_util::ProcessBuilder;
101102use serde:: { Deserialize , Serialize } ;
102103
103104use crate :: util:: errors:: CargoResult ;
104- use crate :: util:: indented_lines;
105+ use crate :: util:: { indented_lines, iter_join } ;
105106use crate :: Config ;
106107
107108pub const SEE_CHANNELS : & str =
@@ -416,13 +417,18 @@ impl Features {
416417 let mut ret = Features :: default ( ) ;
417418 ret. nightly_features_allowed = config. nightly_features_allowed ;
418419 for feature in features {
419- ret. add ( feature, warnings) ?;
420+ ret. add ( feature, config , warnings) ?;
420421 ret. activated . push ( feature. to_string ( ) ) ;
421422 }
422423 Ok ( ret)
423424 }
424425
425- fn add ( & mut self , feature_name : & str , warnings : & mut Vec < String > ) -> CargoResult < ( ) > {
426+ fn add (
427+ & mut self ,
428+ feature_name : & str ,
429+ config : & Config ,
430+ warnings : & mut Vec < String > ,
431+ ) -> CargoResult < ( ) > {
426432 let nightly_features_allowed = self . nightly_features_allowed ;
427433 let ( slot, feature) = match self . status ( feature_name) {
428434 Some ( p) => p,
@@ -470,7 +476,17 @@ impl Features {
470476 SEE_CHANNELS ,
471477 see_docs( )
472478 ) ,
473- Status :: Unstable => { }
479+ Status :: Unstable => {
480+ if let Some ( allow) = & config. cli_unstable ( ) . allow_features {
481+ if !allow. contains ( feature_name) {
482+ bail ! (
483+ "the feature `{}` is not in the list of allowed features: [{}]" ,
484+ feature_name,
485+ iter_join( allow, ", " ) ,
486+ ) ;
487+ }
488+ }
489+ }
474490 Status :: Removed => bail ! (
475491 "the cargo feature `{}` has been removed\n \
476492 Remove the feature from Cargo.toml to remove this error.\n \
@@ -530,37 +546,42 @@ impl Features {
530546#[ derive( Default , Debug , Deserialize ) ]
531547#[ serde( default , rename_all = "kebab-case" ) ]
532548pub struct CliUnstable {
549+ // Permanently unstable features:
550+ pub allow_features : Option < BTreeSet < String > > ,
533551 pub print_im_a_teapot : bool ,
534- pub unstable_options : bool ,
535- pub no_index_update : bool ,
536- pub avoid_dev_deps : bool ,
537- pub minimal_versions : bool ,
552+
553+ // All other unstable features.
554+ // Please keep this list lexiographically ordered.
538555 pub advanced_env : bool ,
539- pub config_include : bool ,
540- pub dual_proc_macros : bool ,
541- pub mtime_on_use : bool ,
542- pub named_profiles : bool ,
556+ pub avoid_dev_deps : bool ,
543557 pub binary_dep_depinfo : bool ,
544558 #[ serde( deserialize_with = "deserialize_build_std" ) ]
545559 pub build_std : Option < Vec < String > > ,
546560 pub build_std_features : Option < Vec < String > > ,
547- pub timings : Option < Vec < String > > ,
548- pub doctest_xcompile : bool ,
561+ pub config_include : bool ,
562+ pub configurable_env : bool ,
563+ pub credential_process : bool ,
549564 pub doctest_in_workspace : bool ,
550- pub panic_abort_tests : bool ,
551- pub jobserver_per_rustc : bool ,
565+ pub doctest_xcompile : bool ,
566+ pub dual_proc_macros : bool ,
567+ pub enable_future_incompat_feature : bool ,
568+ pub extra_link_arg : bool ,
552569 pub features : Option < Vec < String > > ,
553- pub separate_nightlies : bool ,
570+ pub jobserver_per_rustc : bool ,
571+ pub minimal_versions : bool ,
572+ pub mtime_on_use : bool ,
554573 pub multitarget : bool ,
574+ pub named_profiles : bool ,
575+ pub namespaced_features : bool ,
576+ pub no_index_update : bool ,
577+ pub panic_abort_tests : bool ,
578+ pub patch_in_config : bool ,
555579 pub rustdoc_map : bool ,
580+ pub separate_nightlies : bool ,
556581 pub terminal_width : Option < Option < usize > > ,
557- pub namespaced_features : bool ,
582+ pub timings : Option < Vec < String > > ,
583+ pub unstable_options : bool ,
558584 pub weak_dep_features : bool ,
559- pub extra_link_arg : bool ,
560- pub patch_in_config : bool ,
561- pub credential_process : bool ,
562- pub configurable_env : bool ,
563- pub enable_future_incompat_feature : bool ,
564585}
565586
566587const STABILIZED_COMPILE_PROGRESS : & str = "The progress bar is now always \
@@ -627,6 +648,13 @@ impl CliUnstable {
627648 ) ;
628649 }
629650 let mut warnings = Vec :: new ( ) ;
651+ // We read flags twice, first to get allowed-features (if specified),
652+ // and then to read the remaining unstable flags.
653+ for flag in flags {
654+ if flag. starts_with ( "allow-features=" ) {
655+ self . add ( flag, & mut warnings) ?;
656+ }
657+ }
630658 for flag in flags {
631659 self . add ( flag, & mut warnings) ?;
632660 }
@@ -656,6 +684,7 @@ impl CliUnstable {
656684 fn parse_features ( value : Option < & str > ) -> Vec < String > {
657685 match value {
658686 None => Vec :: new ( ) ,
687+ Some ( "" ) => Vec :: new ( ) ,
659688 Some ( v) => v. split ( ',' ) . map ( |s| s. to_string ( ) ) . collect ( ) ,
660689 }
661690 }
@@ -698,8 +727,19 @@ impl CliUnstable {
698727 ) )
699728 } ;
700729
730+ if let Some ( allowed) = & self . allow_features {
731+ if k != "allow-features" && !allowed. contains ( k) {
732+ bail ! (
733+ "the feature `{}` is not in the list of allowed features: [{}]" ,
734+ k,
735+ iter_join( allowed, ", " )
736+ ) ;
737+ }
738+ }
739+
701740 match k {
702741 "print-im-a-teapot" => self . print_im_a_teapot = parse_bool ( k, v) ?,
742+ "allow-features" => self . allow_features = Some ( parse_features ( v) . into_iter ( ) . collect ( ) ) ,
703743 "unstable-options" => self . unstable_options = parse_empty ( k, v) ?,
704744 "no-index-update" => self . no_index_update = parse_empty ( k, v) ?,
705745 "avoid-dev-deps" => self . avoid_dev_deps = parse_empty ( k, v) ?,
0 commit comments