use std::{env, fs};
use std::cell::Cell;
+use std::default::Default;
use std::fs::File;
use std::io::{Error, ErrorKind, Read};
use std::path::{Path, PathBuf};
use lists::{ListTactic, SeparatorPlace, SeparatorTactic};
use Summary;
+
+macro_rules! is_nightly_channel {
+ () => {
+ env::var("CFG_RELEASE_CHANNEL")
+ .map(|c| c == "nightly")
+ .unwrap_or(false)
+ }
+}
+
macro_rules! configuration_option_enum{
($e:ident: $( $x:ident ),+ $(,)*) => {
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
}
}
-configuration_option_enum! { Style:
- Rfc, // Follow the style RFCs style.
- Legacy, // Follow the traditional Rustfmt style.
-}
-
configuration_option_enum! { NewlineStyle:
Windows, // \r\n
Unix, // \n
AlwaysNextLine,
}
-// How to indent a function's return type.
-configuration_option_enum! { ReturnIndent:
- // Aligned with the arguments
- WithArgs,
- // Aligned with the where clause
- WithWhereClause,
-}
-
configuration_option_enum! { IndentStyle:
// First line on the same line as the opening brace, all lines aligned with
// the first line.
Compressed,
// Use more lines.
Tall,
- // Try to compress if the body is empty.
- CompressedIfEmpty,
// Place every item on a separate line.
Vertical,
}
pub fn to_list_tactic(self) -> ListTactic {
match self {
Density::Compressed => ListTactic::Mixed,
- Density::Tall | Density::CompressedIfEmpty => ListTactic::HorizontalVertical,
+ Density::Tall => ListTactic::HorizontalVertical,
Density::Vertical => ListTactic::Vertical,
}
}
}
-configuration_option_enum! { LicensePolicy:
- // Do not place license text at top of files
- NoLicense,
- // Use the text in "license" field as the license
- TextLicense,
- // Use a text file as the license text
- FileLicense,
-}
-
-configuration_option_enum! { MultilineStyle:
- // Use horizontal layout if it fits in one line, fall back to vertical
- PreferSingle,
- // Use vertical layout
- ForceMulti,
-}
-
-impl MultilineStyle {
- pub fn to_list_tactic(self) -> ListTactic {
- match self {
- MultilineStyle::PreferSingle => ListTactic::HorizontalVertical,
- MultilineStyle::ForceMulti => ListTactic::Vertical,
- }
- }
-}
-
configuration_option_enum! { ReportTactic:
Always,
Unnumbered,
Checkstyle,
}
+configuration_option_enum! { Color:
+ // Always use color, whether it is a piped or terminal output
+ Always,
+ // Never use color
+ Never,
+ // Automatically use color, if supported by terminal
+ Auto,
+}
+
+#[derive(Deserialize, Serialize, Clone, Debug)]
+pub struct WidthHeuristics {
+ // Maximum width of the args of a function call before falling back
+ // to vertical formatting.
+ pub fn_call_width: usize,
+ // Maximum width in the body of a struct lit before falling back to
+ // vertical formatting.
+ pub struct_lit_width: usize,
+ // Maximum width in the body of a struct variant before falling back
+ // to vertical formatting.
+ pub struct_variant_width: usize,
+ // Maximum width of an array literal before falling back to vertical
+ // formatting.
+ pub array_width: usize,
+ // Maximum length of a chain to fit on a single line.
+ pub chain_width: usize,
+ // Maximum line length for single line if-else expressions. A value
+ // of zero means always break if-else expressions.
+ pub single_line_if_else_max_width: usize,
+}
+
+impl WidthHeuristics {
+ // Using this WidthHeuristics means we ignore heuristics.
+ fn null() -> WidthHeuristics {
+ WidthHeuristics {
+ fn_call_width: usize::max_value(),
+ struct_lit_width: 0,
+ struct_variant_width: 0,
+ array_width: usize::max_value(),
+ chain_width: usize::max_value(),
+ single_line_if_else_max_width: 0,
+ }
+ }
+}
+
+impl Default for WidthHeuristics {
+ fn default() -> WidthHeuristics {
+ WidthHeuristics {
+ fn_call_width: 60,
+ struct_lit_width: 18,
+ struct_variant_width: 35,
+ array_width: 60,
+ chain_width: 60,
+ single_line_if_else_max_width: 50,
+ }
+ }
+}
+
+impl ::std::str::FromStr for WidthHeuristics {
+ type Err = &'static str;
+
+ fn from_str(_: &str) -> Result<Self, Self::Err> {
+ Err("WidthHeuristics is not parsable")
+ }
+}
+
+impl ::config::ConfigType for WidthHeuristics {
+ fn doc_hint() -> String {
+ String::new()
+ }
+}
+
/// Trait for types that can be used in `Config`.
pub trait ConfigType: Sized {
/// Returns hint text for use in `Config::print_docs()`. For enum types, this is a
}
macro_rules! create_config {
- ($($i:ident: $ty:ty, $def:expr, $( $dstring:expr ),+ );+ $(;)*) => (
+ ($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
#[derive(Clone)]
pub struct Config {
// For each config item, we store a bool indicating whether it has
// been accessed and the value, and a bool whether the option was
// manually initialised, or taken from the default,
- $($i: (Cell<bool>, bool, $ty)),+
+ $($i: (Cell<bool>, bool, $ty, bool)),+
}
// Just like the Config struct but with each property wrapped
// as Option<T>. This is used to parse a rustfmt.toml that doesn't
- // specity all properties of `Config`.
+ // specify all properties of `Config`.
// We first parse into `PartialConfig`, then create a default `Config`
// and overwrite the properties with corresponding values from `PartialConfig`.
#[derive(Deserialize, Serialize, Clone)]
impl PartialConfig {
pub fn to_toml(&self) -> Result<String, String> {
- // file_lines can't be specified in TOML
+ // Non-user-facing options can't be specified in TOML
let mut cloned = self.clone();
cloned.file_lines = None;
+ cloned.verbose = None;
+ cloned.width_heuristics = None;
toml::to_string(&cloned)
.map_err(|e| format!("Could not output config: {}", e.to_string()))
$(
pub fn $i(&mut self, value: $ty) {
(self.0).$i.2 = value;
+ if stringify!($i) == "use_small_heuristics" {
+ self.0.set_heuristics();
+ }
}
)+
}
fn fill_from_parsed_config(mut self, parsed: PartialConfig) -> Config {
$(
if let Some(val) = parsed.$i {
- self.$i.1 = true;
- self.$i.2 = val;
+ if self.$i.3 {
+ self.$i.1 = true;
+ self.$i.2 = val;
+ } else {
+ if is_nightly_channel!() {
+ self.$i.1 = true;
+ self.$i.2 = val;
+ } else {
+ println!("Warning: can't set `{} = {:?}`, unstable features are only \
+ available in nightly channel.", stringify!($i), val);
+ }
+ }
}
)+
+ self.set_heuristics();
self
}
)+
_ => panic!("Unknown config key in override: {}", key)
}
+
+ if key == "use_small_heuristics" {
+ self.set_heuristics();
+ }
}
/// Construct a `Config` from the toml file specified at `file_path`.
$(
println!("{}{}", space_str, $dstring);
)+
- println!("");
+ println!();
)+
}
+
+ fn set_heuristics(&mut self) {
+ if self.use_small_heuristics.2 {
+ self.set().width_heuristics(WidthHeuristics::default());
+ } else {
+ self.set().width_heuristics(WidthHeuristics::null());
+ }
+ }
}
// Template for the default configuration
fn default() -> Config {
Config {
$(
- $i: (Cell::new(false), false, $def),
+ $i: (Cell::new(false), false, $def, $stb),
)+
}
}
///
/// Return the path if a config file exists, empty if no file exists, and Error for IO errors
pub fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
- const CONFIG_FILE_NAMES: [&'static str; 2] = [".rustfmt.toml", "rustfmt.toml"];
+ const CONFIG_FILE_NAMES: [&str; 2] = [".rustfmt.toml", "rustfmt.toml"];
for config_file_name in &CONFIG_FILE_NAMES {
let config_file = dir.join(config_file_name);
match fs::metadata(&config_file) {
Ok(ref md) if md.is_file() => return Ok(Some(config_file)),
// Return the error if it's something other than `NotFound`; otherwise we didn't
// find the project file yet, and continue searching.
- Err(e) => if e.kind() != ErrorKind::NotFound {
- return Err(e);
- },
+ Err(e) => {
+ if e.kind() != ErrorKind::NotFound {
+ return Err(e);
+ }
+ }
_ => {}
}
}
create_config! {
- verbose: bool, false, "Use verbose output";
- disable_all_formatting: bool, false, "Don't reformat anything";
- skip_children: bool, false, "Don't reformat out of line modules";
- file_lines: FileLines, FileLines::all(),
- "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
- via the --file-lines option";
- max_width: usize, 100, "Maximum width of each line";
- error_on_line_overflow: bool, true, "Error if unable to get all lines within max_width";
- error_on_line_overflow_comments: bool, true, "Error if unable to get comments within max_width";
- tab_spaces: usize, 4, "Number of spaces per tab";
- fn_call_width: usize, 60,
- "Maximum width of the args of a function call before falling back to vertical formatting";
- struct_lit_width: usize, 18,
- "Maximum width in the body of a struct lit before falling back to vertical formatting";
- struct_variant_width: usize, 35,
- "Maximum width in the body of a struct variant before falling back to vertical formatting";
- force_explicit_abi: bool, true, "Always print the abi for extern items";
- newline_style: NewlineStyle, NewlineStyle::Unix, "Unix or Windows line endings";
- fn_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for functions";
- item_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for structs and enums";
- control_style: Style, Style::Rfc, "Indent style for control flow statements";
- control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine,
+ // Fundamental stuff
+ max_width: usize, 100, true, "Maximum width of each line";
+ hard_tabs: bool, false, true, "Use tab characters for indentation, spaces for alignment";
+ tab_spaces: usize, 4, true, "Number of spaces per tab";
+ newline_style: NewlineStyle, NewlineStyle::Unix, true, "Unix or Windows line endings";
+ indent_style: IndentStyle, IndentStyle::Block, false, "How do we indent expressions or items.";
+ use_small_heuristics: bool, true, false, "Whether to use different formatting for items and\
+ expressions if they satisfy a heuristic notion of 'small'.";
+
+ // strings and comments
+ format_strings: bool, false, false, "Format string literals where necessary";
+ wrap_comments: bool, false, true, "Break comments to fit on the line";
+ comment_width: usize, 80, false,
+ "Maximum length of comments. No effect unless wrap_comments = true";
+ normalize_comments: bool, false, true, "Convert /* */ comments to // comments where possible";
+
+ // Single line expressions and items.
+ empty_item_single_line: bool, true, false,
+ "Put empty-body functions and impls on a single line";
+ struct_lit_single_line: bool, true, false,
+ "Put small struct literals on a single line";
+ fn_single_line: bool, false, false, "Put single-expression functions on a single line";
+ where_single_line: bool, false, false, "To force single line where layout";
+
+ // Imports
+ imports_indent: IndentStyle, IndentStyle::Visual, false, "Indent of imports";
+ imports_layout: ListTactic, ListTactic::Mixed, false, "Item layout inside a import block";
+
+ // Ordering
+ reorder_extern_crates: bool, true, false, "Reorder extern crate statements alphabetically";
+ reorder_extern_crates_in_group: bool, true, false, "Reorder extern crate statements in group";
+ reorder_imports: bool, false, false, "Reorder import statements alphabetically";
+ reorder_imports_in_group: bool, false, false, "Reorder import statements in group";
+ reorder_imported_names: bool, true, false,
+ "Reorder lists of names in import statements alphabetically";
+
+ // Spaces around punctuation
+ binop_separator: SeparatorPlace, SeparatorPlace::Front, false,
+ "Where to put a binary operator when a binary expression goes multiline.";
+ type_punctuation_density: TypeDensity, TypeDensity::Wide, false,
+ "Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
+ space_before_colon: bool, false, false, "Leave a space before the colon";
+ space_after_colon: bool, true, false, "Leave a space after the colon";
+ spaces_around_ranges: bool, false, false, "Put spaces around the .. and ... range operators";
+ spaces_within_parens_and_brackets: bool, false, false,
+ "Put spaces within non-empty parentheses or brackets";
+
+ // Misc.
+ combine_control_expr: bool, true, false, "Combine control expressions with function calls.";
+ struct_field_align_threshold: usize, 0, false, "Align struct fields if their diffs fits within \
+ threshold.";
+ remove_blank_lines_at_start_or_end_of_block: bool, true, false,
+ "Remove blank lines at start or end of a block";
+ same_line_attributes: bool, true, false,
+ "Try to put attributes on the same line as fields and variants.";
+ match_arm_blocks: bool, true, false, "Wrap the body of arms in blocks when it does not fit on \
+ the same line with the pattern of arms";
+ force_multiline_blocks: bool, false, false,
+ "Force multiline closure bodies and match arms to be wrapped in a block";
+ fn_args_density: Density, Density::Tall, false, "Argument density in functions";
+ brace_style: BraceStyle, BraceStyle::SameLineWhere, false, "Brace style for items";
+ control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, false,
"Brace style for control flow constructs";
- impl_empty_single_line: bool, true, "Put empty-body implementations on a single line";
- trailing_comma: SeparatorTactic, SeparatorTactic::Vertical,
+ trailing_comma: SeparatorTactic, SeparatorTactic::Vertical, false,
"How to handle trailing commas for lists";
- trailing_semicolon: bool, true, "Add trailing semicolon after break, continue and return";
- fn_empty_single_line: bool, true, "Put empty-body functions on a single line";
- fn_single_line: bool, false, "Put single-expression functions on a single line";
- fn_return_indent: ReturnIndent, ReturnIndent::WithArgs,
- "Location of return type in function declaration";
- fn_args_paren_newline: bool, false, "If function argument parenthesis goes on a newline";
- fn_args_density: Density, Density::Tall, "Argument density in functions";
- fn_args_layout: IndentStyle, IndentStyle::Block,
- "Layout of function arguments and tuple structs";
- array_layout: IndentStyle, IndentStyle::Block, "Indent on arrays";
- array_width: usize, 60,
- "Maximum width of an array literal before falling back to vertical formatting";
- array_horizontal_layout_threshold: usize, 0,
- "How many elements array must have before rustfmt uses horizontal layout.";
- type_punctuation_density: TypeDensity, TypeDensity::Wide,
- "Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
- where_style: Style, Style::Rfc, "Overall strategy for where clauses";
- // TODO:
- // 1. Should we at least try to put the where clause on the same line as the rest of the
- // function decl?
- // 2. Currently options `Tall` and `Vertical` produce the same output.
- where_density: Density, Density::Vertical, "Density of a where clause";
- where_layout: ListTactic, ListTactic::Vertical, "Element layout inside a where clause";
- where_pred_indent: IndentStyle, IndentStyle::Visual,
- "Indentation style of a where predicate";
- generics_indent: IndentStyle, IndentStyle::Block, "Indentation of generics";
- struct_lit_style: IndentStyle, IndentStyle::Block, "Style of struct definition";
- struct_lit_multiline_style: MultilineStyle, MultilineStyle::PreferSingle,
- "Multiline style on literal structs";
- fn_call_style: IndentStyle, IndentStyle::Block, "Indentation for function calls, etc.";
- report_todo: ReportTactic, ReportTactic::Never,
- "Report all, none or unnumbered occurrences of TODO in source file comments";
- report_fixme: ReportTactic, ReportTactic::Never,
- "Report all, none or unnumbered occurrences of FIXME in source file comments";
- chain_indent: IndentStyle, IndentStyle::Block, "Indentation of chain";
- chain_one_line_max: usize, 60, "Maximum length of a chain to fit on a single line";
- chain_split_single_child: bool, false, "Split a chain with a single child if its length \
- exceeds `chain_one_line_max`";
- imports_indent: IndentStyle, IndentStyle::Visual, "Indent of imports";
- imports_layout: ListTactic, ListTactic::Mixed, "Item layout inside a import block";
- reorder_extern_crates: bool, true, "Reorder extern crate statements alphabetically";
- reorder_extern_crates_in_group: bool, true, "Reorder extern crate statements in group";
- reorder_imports: bool, false, "Reorder import statements alphabetically";
- reorder_imports_in_group: bool, false, "Reorder import statements in group";
- reorder_imported_names: bool, true,
- "Reorder lists of names in import statements alphabetically";
- single_line_if_else_max_width: usize, 50, "Maximum line length for single line if-else \
- expressions. A value of zero means always break \
- if-else expressions.";
- format_strings: bool, false, "Format string literals where necessary";
- force_format_strings: bool, false, "Always format string literals";
- take_source_hints: bool, false, "Retain some formatting characteristics from the source code";
- hard_tabs: bool, false, "Use tab characters for indentation, spaces for alignment";
- wrap_comments: bool, false, "Break comments to fit on the line";
- comment_width: usize, 80, "Maximum length of comments. No effect unless wrap_comments = true";
- normalize_comments: bool, false, "Convert /* */ comments to // comments where possible";
- wrap_match_arms: bool, true, "Wrap the body of arms in blocks when it does not fit on \
- the same line with the pattern of arms";
- match_block_trailing_comma: bool, false,
+ trailing_semicolon: bool, true, false,
+ "Add trailing semicolon after break, continue and return";
+ match_block_trailing_comma: bool, false, false,
"Put a trailing comma after a block based match arm (non-block arms are not affected)";
- match_arm_forces_newline: bool, false,
- "Force match arm bodies to be in a new lines";
- indent_match_arms: bool, true, "Indent match arms instead of keeping them at the same \
- indentation level as the match keyword";
- match_pattern_separator_break_point: SeparatorPlace, SeparatorPlace::Back,
- "Put a match sub-patterns' separator in front or back.";
- closure_block_indent_threshold: isize, 7, "How many lines a closure must have before it is \
- block indented. -1 means never use block indent.";
- space_before_type_annotation: bool, false,
- "Leave a space before the colon in a type annotation";
- space_after_type_annotation_colon: bool, true,
- "Leave a space after the colon in a type annotation";
- space_before_struct_lit_field_colon: bool, false,
- "Leave a space before the colon in a struct literal field";
- space_after_struct_lit_field_colon: bool, true,
- "Leave a space after the colon in a struct literal field";
- space_before_bound: bool, false, "Leave a space before the colon in a trait or lifetime bound";
- space_after_bound_colon: bool, true,
- "Leave a space after the colon in a trait or lifetime bound";
- spaces_around_ranges: bool, false, "Put spaces around the .. and ... range operators";
- spaces_within_angle_brackets: bool, false, "Put spaces within non-empty generic arguments";
- spaces_within_square_brackets: bool, false, "Put spaces within non-empty square brackets";
- spaces_within_parens: bool, false, "Put spaces within non-empty parentheses";
- use_try_shorthand: bool, false, "Replace uses of the try! macro by the ? shorthand";
- write_mode: WriteMode, WriteMode::Overwrite,
+
+ // Options that can change the source code beyond whitespace/blocks (somewhat linty things)
+ merge_derives: bool, true, true, "Merge multiple `#[derive(...)]` into a single one";
+ use_try_shorthand: bool, false, false, "Replace uses of the try! macro by the ? shorthand";
+ condense_wildcard_suffixes: bool, false, false, "Replace strings of _ wildcards by a single .. \
+ in tuple patterns";
+ force_explicit_abi: bool, true, true, "Always print the abi for extern items";
+
+ // Control options (changes the operation of rustfmt, rather than the formatting)
+ write_mode: WriteMode, WriteMode::Overwrite, false,
"What Write Mode to use when none is supplied: \
Replace, Overwrite, Display, Plain, Diff, Coverage";
- condense_wildcard_suffixes: bool, false, "Replace strings of _ wildcards by a single .. in \
- tuple patterns";
- combine_control_expr: bool, true, "Combine control expressions with function calls.";
- struct_field_align_threshold: usize, 0, "Align struct fields if their diffs fits within \
- threshold.";
- remove_blank_lines_at_start_or_end_of_block: bool, true,
- "Remove blank lines at start or end of a block";
- attributes_on_same_line_as_field: bool, true,
- "Try to put attributes on the same line as fields.";
- attributes_on_same_line_as_variant: bool, true,
- "Try to put attributes on the same line as variants in enum declarations.";
- multiline_closure_forces_block: bool, false,
- "Force multiline closure bodies to be wrapped in a block";
- multiline_match_arm_forces_block: bool, false,
- "Force multiline match arm bodies to be wrapped in a block";
- merge_derives: bool, true, "Merge multiple `#[derive(...)]` into a single one";
- binop_separator: SeparatorPlace, SeparatorPlace::Front,
- "Where to put a binary operator when a binary expression goes multiline.";
- required_version: String, env!("CARGO_PKG_VERSION").to_owned(),
- "Require a specific version of rustfmt."
+ color: Color, Color::Auto, false,
+ "What Color option to use when none is supplied: Always, Never, Auto";
+ required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false,
+ "Require a specific version of rustfmt.";
+ unstable_features: bool, false, true,
+ "Enables unstable features. Only available on nightly channel";
+ disable_all_formatting: bool, false, false, "Don't reformat anything";
+ skip_children: bool, false, false, "Don't reformat out of line modules";
+ error_on_line_overflow: bool, true, false, "Error if unable to get all lines within max_width";
+ error_on_line_overflow_comments: bool, true, false,
+ "Error if unable to get comments within max_width";
+ report_todo: ReportTactic, ReportTactic::Never, false,
+ "Report all, none or unnumbered occurrences of TODO in source file comments";
+ report_fixme: ReportTactic, ReportTactic::Never, false,
+ "Report all, none or unnumbered occurrences of FIXME in source file comments";
+
+ // Not user-facing.
+ verbose: bool, false, false, "Use verbose output";
+ file_lines: FileLines, FileLines::all(), false,
+ "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
+ via the --file-lines option";
+ width_heuristics: WidthHeuristics, WidthHeuristics::default(), false,
+ "'small' heuristic values";
}
#[cfg(test)]
fn test_config_used_to_toml() {
let config = Config::default();
- let verbose = config.verbose();
+ let merge_derives = config.merge_derives();
let skip_children = config.skip_children();
let used_options = config.used_options();
let toml = used_options.to_toml().unwrap();
assert_eq!(
toml,
- format!("verbose = {}\nskip_children = {}\n", verbose, skip_children)
+ format!(
+ "merge_derives = {}\nskip_children = {}\n",
+ merge_derives, skip_children,
+ )
);
}
assert_eq!(config.was_set().hard_tabs(), true);
assert_eq!(config.was_set().verbose(), false);
}
+
+ // FIXME(#2183) these tests cannot be run in parallel because they use env vars
+ // #[test]
+ // fn test_as_not_nightly_channel() {
+ // let mut config = Config::default();
+ // assert_eq!(config.was_set().unstable_features(), false);
+ // config.set().unstable_features(true);
+ // assert_eq!(config.was_set().unstable_features(), false);
+ // }
+
+ // #[test]
+ // fn test_as_nightly_channel() {
+ // let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from(""));
+ // ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly");
+ // let mut config = Config::default();
+ // config.set().unstable_features(true);
+ // assert_eq!(config.was_set().unstable_features(), false);
+ // config.set().unstable_features(true);
+ // assert_eq!(config.unstable_features(), true);
+ // ::std::env::set_var("CFG_RELEASE_CHANNEL", v);
+ // }
+
+ // #[test]
+ // fn test_unstable_from_toml() {
+ // let mut config = Config::from_toml("unstable_features = true").unwrap();
+ // assert_eq!(config.was_set().unstable_features(), false);
+ // let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from(""));
+ // ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly");
+ // config = Config::from_toml("unstable_features = true").unwrap();
+ // assert_eq!(config.was_set().unstable_features(), true);
+ // assert_eq!(config.unstable_features(), true);
+ // ::std::env::set_var("CFG_RELEASE_CHANNEL", v);
+ // }
}