Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow rustfmt to abort when using nightly only features on stable channel #5025

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Add new configuration option abort_on_unrecognised_options
This option was proposed in issue 5022 and allows rustfmt to exit early
in the event that any unstable options are set in the projects
rustfmt.toml
  • Loading branch information
ytmimi committed Jul 6, 2022
commit 6f4a59831c93049a35568a7de71aace595e85eb3
9 changes: 9 additions & 0 deletions Configurations.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 17,15 @@ To enable unstable options, set `unstable_features = true` in `rustfmt.toml` or

Below you find a detailed visual guide on all the supported configuration options of rustfmt:

## `abort_on_unrecognised_options`

Exit early when using nightly only options on the stable channel

- **Default value**: `false`
- **Possible values**: `true`, `false`
- **Stable**: No (tracking issue: [#5022](https://github.com/rust-lang/rustfmt/issues/5022))


## `array_width`

Maximum width of an array literal before falling back to vertical formatting.
Expand Down
142 changes: 120 additions & 22 deletions src/config/config_type.rs
Original file line number Diff line number Diff line change
@@ -1,3 1,5 @@
use std::collections::HashMap;

use crate::config::file_lines::FileLines;
use crate::config::options::{IgnoreList, WidthHeuristics};

Expand Down Expand Up @@ -58,6 60,82 @@ impl ConfigType for IgnoreList {
}
}

/// Store a map of all Unstable options used in in the configuration.
#[derive(Clone, Debug)]
pub struct UnstableOptions {
pub(crate) options: HashMap<&'static str, String>,
}

impl std::fmt::Display for UnstableOptions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(message) = self.abort_message() {
write!(f, "{}", message)
} else {
write!(f, "No unstable options were used.")
}
}
}

impl UnstableOptions {
/// Create a new UnstableOptions struct
pub(crate) fn new() -> Self {
Self {
options: HashMap::new(),
}
}

/// Insert an unstable option and a user supplied value for that unstable option
pub(crate) fn insert(&mut self, option: &'static str, user_supplied_value: String) {
self.options.insert(option, user_supplied_value);
}

/// Check if any unstable options have been set
pub(crate) fn has_unstable_options(&self) -> bool {
!self.options.is_empty()
}

/// Generate the Warning message
pub(crate) fn warning_message(&self) -> Option<String> {
if self.options.is_empty() {
return None;
}
let mut result = String::new();

for (k, v) in self.options.iter() {
result.push_str(&format!(
"Warning: can't set `{} = {}`, unstable features are only \
available in nightly channel.\n",
k, v,
));
}

let upgrade_to_abort_message = "\nSet `abort_on_unrecognised_options = true` \
to convert this warning into an error\n\n";

result.push_str(upgrade_to_abort_message);

Some(result)
}

/// Generate the Abort message
pub(crate) fn abort_message(&self) -> Option<String> {
if self.options.is_empty() {
return None;
}

let mut result = String::new();
result.push_str("Can't set nightly options when using stable rustfmt\n");

for (k, v) in self.options.iter() {
result.push_str(&format!(" - `{} = {}`\n", k, v));
}
let to_warning_message = "\nSet `abort_on_unrecognised_options = false` \
to convert this error into a warning\n\n";
result.push_str(to_warning_message);
Some(result)
}
}

macro_rules! create_config {
// Options passed in to the macro.
//
Expand All @@ -76,6 154,8 @@ macro_rules! create_config {
#[derive(Clone)]
#[allow(unreachable_pub)]
pub struct Config {
// Unstable Options specified on the stable channel
configured_unstable_options: UnstableOptions,
// For each config item, we store:
//
// - 0: true if the value has been access
Expand Down Expand Up @@ -159,12 239,41 @@ macro_rules! create_config {
ConfigWasSet(self)
}

/// Insert all unstable options and their values into the UnstableOptions struct.
/// The only exception is the "abort_on_unrecognised_options", which helps
/// determine if we should abort or warn when using unstable options on stable rustfmt
#[allow(unreachable_pub)]
pub fn insert_unstable_options(&mut self, option: &'static str, value: String) {
if option == "abort_on_unrecognised_options" {
return
}

match option {
$(
stringify!($i) => {
// If its an unstable option then add it to the unstable list
if !self.$i.3 {
self.configured_unstable_options.insert(option, value);
}
}
)
_ => panic!("Unknown config key in override: {}", option)
}

}

fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config {
$(
if let Some(option_value) = parsed.$i {
let option_stable = self.$i.3;
if !option_stable || !option_value.stable_variant() {
self.insert_unstable_options(
stringify!($i), format!("{:?}", &option_value)
);
}

if $crate::config::config_type::is_stable_option_and_value(
stringify!($i), option_stable, &option_value
option_stable, &option_value
) {
self.$i.1 = true;
self.$i.2 = option_value;
Expand Down Expand Up @@ -228,6 337,12 @@ macro_rules! create_config {
}
}

/// Get a reference to the UnstableOptions set on the configuration.
#[allow(unreachable_pub)]
pub fn unstable_options(&self) -> &UnstableOptions {
&self.configured_unstable_options
}

#[allow(unreachable_pub)]
pub fn override_value(&mut self, key: &str, val: &str)
{
Expand Down Expand Up @@ -439,6 554,7 @@ macro_rules! create_config {
impl Default for Config {
fn default() -> Config {
Config {
configured_unstable_options: UnstableOptions::new(),
$(
$i: (Cell::new(false), false, $def, $stb),
)
Expand All @@ -448,35 564,17 @@ macro_rules! create_config {
)
}

pub(crate) fn is_stable_option_and_value<T>(
option_name: &str,
option_stable: bool,
option_value: &T,
) -> bool
pub(crate) fn is_stable_option_and_value<T>(option_stable: bool, option_value: &T) -> bool
where
T: PartialEq std::fmt::Debug ConfigType,
{
let nightly = crate::is_nightly_channel!();
let variant_stable = option_value.stable_variant();
match (nightly, option_stable, variant_stable) {
// Stable with an unstable option
(false, false, _) => {
eprintln!(
"Warning: can't set `{} = {:?}`, unstable features are only \
available in nightly channel.",
option_name, option_value
);
false
}
(false, false, _) => false,
// Stable with a stable option, but an unstable variant
(false, true, false) => {
eprintln!(
"Warning: can't set `{} = {:?}`, unstable variants are only \
available in nightly channel.",
option_name, option_value
);
false
}
(false, true, false) => false,
// Nightly: everything allowed
// Stable with stable option and variant: allowed
(true, _, _) | (false, true, true) => true,
Expand Down
58 changes: 58 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 9,8 @@ use thiserror::Error;

use crate::config::config_type::ConfigType;
#[allow(unreachable_pub)]
pub use crate::config::config_type::UnstableOptions;
#[allow(unreachable_pub)]
pub use crate::config::file_lines::{FileLines, FileName, Range};
#[allow(unreachable_pub)]
pub use crate::config::lists::*;
Expand Down Expand Up @@ -164,6 166,8 @@ create_config! {
or they are left with trailing whitespaces";
ignore: IgnoreList, IgnoreList::default(), false,
"Skip formatting the specified files and directories";
abort_on_unrecognised_options: bool, false, false,
"Exit early when using nightly only options on the stable channel";

// Not user-facing
verbose: Verbosity, Verbosity::Normal, false, "How much to information to emit to the user";
Expand Down Expand Up @@ -586,6 590,59 @@ mod test {
assert_eq!(s.contains(PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION), true);
}

#[stable_only_test]
#[test]
fn test_get_all_unstable_options_set_in_toml() {
use std::collections::HashMap;
let toml = r#"
reorder_impl_items = true
"#;
let config = Config::from_toml(toml, Path::new("")).unwrap();
let mut expected = HashMap::new();
expected.insert("reorder_impl_items", String::from("true"));
assert_eq!(config.unstable_options().options, expected);
}

#[stable_only_test]
#[test]
fn test_warning_message_when_using_unstable_options() {
let toml = r#"
reorder_impl_items = true
"#;
let config = Config::from_toml(toml, Path::new("")).unwrap();
let warning = "\
Warning: can't set `reorder_impl_items = true`, unstable features are only available in \
nightly channel.

Set `abort_on_unrecognised_options = true` to convert this warning into an error

";
assert_eq!(
warning,
config.unstable_options().warning_message().unwrap()
)
}

#[stable_only_test]
#[test]
fn test_abort_message_when_using_unstable_options() {
let toml = r#"
reorder_impl_items = true
"#;
let config = Config::from_toml(toml, Path::new("")).unwrap();
let abort_message = "\
Can't set nightly options when using stable rustfmt
- `reorder_impl_items = true`

Set `abort_on_unrecognised_options = false` to convert this error into a warning

";
assert_eq!(
abort_message,
config.unstable_options().abort_message().unwrap()
)
}

#[test]
fn test_dump_default_config() {
let default_config = format!(
Expand Down Expand Up @@ -663,6 720,7 @@ hide_parse_errors = false
error_on_line_overflow = false
error_on_unformatted = false
ignore = []
abort_on_unrecognised_options = false
emit_mode = "Files"
make_backup = false
"#,
Expand Down
1 change: 1 addition & 0 deletions src/format_report_formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 144,7 @@ fn error_kind_to_snippet_annotation_type(error_kind: &ErrorKind) -> AnnotationTy
| ErrorKind::LostComment
| ErrorKind::BadAttr
| ErrorKind::InvalidGlobPattern(_)
| ErrorKind::NightlyOnlyOptions(_)
| ErrorKind::VersionMismatch => AnnotationType::Error,
ErrorKind::DeprecatedAttr => AnnotationType::Warning,
}
Expand Down
14 changes: 14 additions & 0 deletions src/formatting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 35,20 @@ impl<'b, T: Write 'b> Session<'b, T> {
return Err(ErrorKind::VersionMismatch);
}

if !crate::is_nightly_channel!() {
let using_unstalbe_options = self.config.unstable_options().has_unstable_options();
let abort_on_unstable_options = self.config.abort_on_unrecognised_options();
if using_unstalbe_options && abort_on_unstable_options {
return Err(ErrorKind::NightlyOnlyOptions(
self.config.unstable_options().clone(),
));
} else if using_unstalbe_options && !abort_on_unstable_options {
if let Some(warning) = self.config.unstable_options().warning_message() {
eprintln!("{}", warning);
}
}
}

rustc_span::create_session_if_not_set_then(self.config.edition().into(), |_| {
if self.config.disable_all_formatting() {
// When the input is from stdin, echo back the input.
Expand Down
5 changes: 4 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 46,7 @@ use crate::utils::indent_next_line;

pub use crate::config::{
load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle,
Range, Verbosity,
Range, UnstableOptions, Verbosity,
};

pub use crate::format_report_formatter::{FormatReportFormatter, FormatReportFormatterBuilder};
Expand Down Expand Up @@ -133,6 133,9 @@ pub enum ErrorKind {
/// Invalid glob pattern in `ignore` configuration option.
#[error("Invalid glob pattern found in ignore list: {0}")]
InvalidGlobPattern(ignore::Error),
/// Using unstable, nightly only options on stable rustfmt.
#[error("{0}")]
NightlyOnlyOptions(UnstableOptions),
}

impl ErrorKind {
Expand Down
3 changes: 3 additions & 0 deletions tests/config/no_unstable_options.toml
Original file line number Diff line number Diff line change
@@ -0,0 1,3 @@
max_width = 100
hard_tabs = false
tab_spaces = 4
2 changes: 2 additions & 0 deletions tests/config/some_unstable_options.toml
Original file line number Diff line number Diff line change
@@ -0,0 1,2 @@
wrap_comments = true
unstable_features = true
Loading