Skip to content

Commit

Permalink
Rollup merge of rust-lang#59169 - tmandry:allow-features-flag, r=cram…
Browse files Browse the repository at this point in the history
…ertj

Add `-Z allow_features=...` flag

Adds a compiler option to allow only whitelisted features.

For projects on nightly that want to prevent feature-creep (and maybe, someday, move off of nightly). Not being able to enforce this has been a problem on Fuchsia and at other big companies.

This doesn't support filtering edition feature flags, but someone is welcome to add that if they need it.
  • Loading branch information
kennytm committed Mar 16, 2019
2 parents 25e4366 + 7c59ce9 commit 8601c79
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 4 deletions.
19 changes: 19 additions & 0 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,7 @@ macro_rules! options {
pub const parse_opt_pathbuf: Option<&str> = Some("a path");
pub const parse_list: Option<&str> = Some("a space-separated list of strings");
pub const parse_opt_list: Option<&str> = Some("a space-separated list of strings");
pub const parse_opt_comma_list: Option<&str> = Some("a comma-separated list of strings");
pub const parse_uint: Option<&str> = Some("a number");
pub const parse_passes: Option<&str> =
Some("a space-separated list of passes, or `all`");
Expand Down Expand Up @@ -926,6 +927,18 @@ macro_rules! options {
}
}

fn parse_opt_comma_list(slot: &mut Option<Vec<String>>, v: Option<&str>)
-> bool {
match v {
Some(s) => {
let v = s.split(',').map(|s| s.to_string()).collect();
*slot = Some(v);
true
},
None => false,
}
}

fn parse_uint(slot: &mut usize, v: Option<&str>) -> bool {
match v.and_then(|s| s.parse().ok()) {
Some(i) => { *slot = i; true },
Expand Down Expand Up @@ -1427,6 +1440,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
merge_functions: Option<MergeFunctions> = (None, parse_merge_functions, [TRACKED],
"control the operation of the MergeFunctions LLVM pass, taking
the same values as the target option of the same name"),
allow_features: Option<Vec<String>> = (None, parse_opt_comma_list, [TRACKED],
"only allow the listed language features to be enabled in code (space separated)"),
}

pub fn default_lib_output() -> CrateType {
Expand Down Expand Up @@ -3273,6 +3288,10 @@ mod tests {
opts = reference.clone();
opts.debugging_opts.merge_functions = Some(MergeFunctions::Disabled);
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());

opts = reference.clone();
opts.debugging_opts.allow_features = Some(vec![String::from("lang_items")]);
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
}

#[test]
Expand Down
1 change: 1 addition & 0 deletions src/librustc_interface/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ pub fn register_plugins<'a>(
krate,
&sess.parse_sess,
sess.edition(),
&sess.opts.debugging_opts.allow_features,
);
// these need to be set "early" so that expansion sees `quote` if enabled.
sess.init_features(features);
Expand Down
6 changes: 3 additions & 3 deletions src/libsyntax/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ pub struct StripUnconfigured<'a> {
}

// `cfg_attr`-process the crate's attributes and compute the crate's features.
pub fn features(mut krate: ast::Crate, sess: &ParseSess, edition: Edition)
-> (ast::Crate, Features) {
pub fn features(mut krate: ast::Crate, sess: &ParseSess, edition: Edition,
allow_features: &Option<Vec<String>>) -> (ast::Crate, Features) {
let features;
{
let mut strip_unconfigured = StripUnconfigured {
Expand All @@ -43,7 +43,7 @@ pub fn features(mut krate: ast::Crate, sess: &ParseSess, edition: Edition)
return (krate, Features::new());
}

features = get_features(&sess.span_diagnostic, &krate.attrs, edition);
features = get_features(&sess.span_diagnostic, &krate.attrs, edition, allow_features);

// Avoid reconfiguring malformed `cfg_attr`s
if err_count == sess.span_diagnostic.err_count() {
Expand Down
15 changes: 15 additions & 0 deletions src/libsyntax/diagnostic_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,21 @@ Erroneous code example:
"##,

E0725: r##"
A feature attribute named a feature that was disallowed in the compiler
command line flags.
Erroneous code example:
```ignore (can't specify compiler flags from doctests)
#![feature(never_type)] // error: the feature `never_type` is not in
// the list of allowed features
```
Delete the offending feature attribute, or add it to the list of allowed
features in the `-Z allow_features` flag.
"##,

}

register_diagnostics! {
Expand Down
11 changes: 10 additions & 1 deletion src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2008,7 +2008,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
}

pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
crate_edition: Edition) -> Features {
crate_edition: Edition, allow_features: &Option<Vec<String>>) -> Features {
fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) {
let mut err = struct_span_err!(span_handler, span, E0557, "feature has been removed");
if let Some(reason) = reason {
Expand Down Expand Up @@ -2127,6 +2127,15 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
}

if let Some((.., set)) = ACTIVE_FEATURES.iter().find(|f| name == f.0) {
if let Some(allowed) = allow_features.as_ref() {
if allowed.iter().find(|f| *f == name.as_str()).is_none() {
span_err!(span_handler, mi.span, E0725,
"the feature `{}` is not in the list of allowed features",
name);
continue;
}
}

set(&mut features, mi.span);
features.declared_lang_features.push((name, mi.span, None));
continue
Expand Down
10 changes: 10 additions & 0 deletions src/test/ui/feature-gate/allow-features-empty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// compile-flags: -Z allow_features=
// Note: This test uses rustc internal flags because they will never stabilize.

#![feature(rustc_diagnostic_macros)] //~ ERROR

#![feature(rustc_const_unstable)] //~ ERROR

#![feature(lang_items)] //~ ERROR

fn main() {}
21 changes: 21 additions & 0 deletions src/test/ui/feature-gate/allow-features-empty.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0725]: the feature `rustc_diagnostic_macros` is not in the list of allowed features
--> $DIR/allow-features-empty.rs:4:12
|
LL | #![feature(rustc_diagnostic_macros)]
| ^^^^^^^^^^^^^^^^^^^^^^^

error[E0725]: the feature `rustc_const_unstable` is not in the list of allowed features
--> $DIR/allow-features-empty.rs:6:12
|
LL | #![feature(rustc_const_unstable)]
| ^^^^^^^^^^^^^^^^^^^^

error[E0725]: the feature `lang_items` is not in the list of allowed features
--> $DIR/allow-features-empty.rs:8:12
|
LL | #![feature(lang_items)]
| ^^^^^^^^^^

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0725`.
10 changes: 10 additions & 0 deletions src/test/ui/feature-gate/allow-features.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// compile-flags: -Z allow_features=rustc_diagnostic_macros,lang_items
// Note: This test uses rustc internal flags because they will never stabilize.

#![feature(rustc_diagnostic_macros)]

#![feature(rustc_const_unstable)] //~ ERROR

#![feature(lang_items)]

fn main() {}
9 changes: 9 additions & 0 deletions src/test/ui/feature-gate/allow-features.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0725]: the feature `rustc_const_unstable` is not in the list of allowed features
--> $DIR/allow-features.rs:6:12
|
LL | #![feature(rustc_const_unstable)]
| ^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0725`.

0 comments on commit 8601c79

Please sign in to comment.