diff --git a/Configurations.md b/Configurations.md index ac5747800b25..c1e967c85da2 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1098,7 +1098,7 @@ See also [`format_macro_bodies`](#format_macro_bodies). ## `format_macro_bodies` -Format the bodies of macros. +Format the bodies of declarative macro definitions. - **Default value**: `true` - **Possible values**: `true`, `false` @@ -1248,12 +1248,20 @@ Control the case of the letters in hexadecimal literal values ## `hide_parse_errors` -Do not show parse errors if the parser failed to parse files. +This option is deprecated and has been renamed to `show_parse_errors` to avoid confusion around the double negative default of `hide_parse_errors=false`. - **Default value**: `false` - **Possible values**: `true`, `false` - **Stable**: No (tracking issue: [#3390](https://github.com/rust-lang/rustfmt/issues/3390)) +## `show_parse_errors` + +Show parse errors if the parser failed to parse files. + +- **Default value**: `true` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: [#5977](https://github.com/rust-lang/rustfmt/issues/5977)) + ## `ignore` Skip formatting files and directories that match the specified pattern. diff --git a/ci/check_diff.sh b/ci/check_diff.sh index 50c58b1f4925..42fdcaa64246 100755 --- a/ci/check_diff.sh +++ b/ci/check_diff.sh @@ -2,9 +2,6 @@ set -e -# https://github.com/rust-lang/rustfmt/issues/5675 -export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib:$LD_LIBRARY_PATH - function print_usage() { echo "usage check_diff REMOTE_REPO FEATURE_BRANCH [COMMIT_HASH] [OPTIONAL_RUSTFMT_CONFIGS]" } @@ -114,15 +111,42 @@ function compile_rustfmt() { git remote add feature $REMOTE_REPO git fetch feature $FEATURE_BRANCH - cargo build --release --bin rustfmt && cp target/release/rustfmt $1/rustfmt + CARGO_VERSON=$(cargo --version) + echo -e "\ncompiling with $CARGO_VERSON\n" + + # Because we're building standalone binaries we need to set `LD_LIBRARY_PATH` so each + # binary can find it's runtime dependencies. See https://github.com/rust-lang/rustfmt/issues/5675 + # This will prepend the `LD_LIBRARY_PATH` for the master rustfmt binary + export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib:$LD_LIBRARY_PATH + + echo "Building rustfmt from src" + cargo build -q --release --bin rustfmt && cp target/release/rustfmt $1/rustfmt + if [ -z "$OPTIONAL_COMMIT_HASH" ] || [ "$FEATURE_BRANCH" = "$OPTIONAL_COMMIT_HASH" ]; then git switch $FEATURE_BRANCH else git switch $OPTIONAL_COMMIT_HASH --detach fi - cargo build --release --bin rustfmt && cp target/release/rustfmt $1/feature_rustfmt + + # This will prepend the `LD_LIBRARY_PATH` for the feature branch rustfmt binary. + # In most cases the `LD_LIBRARY_PATH` should be the same for both rustfmt binaries that we build + # in `compile_rustfmt`, however, there are scenarios where each binary has different runtime + # dependencies. For example, during subtree syncs we bump the nightly toolchain required to build + # rustfmt, and therefore the feature branch relies on a newer set of runtime dependencies. + export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib:$LD_LIBRARY_PATH + + echo "Building feature rustfmt from src" + cargo build -q --release --bin rustfmt && cp target/release/rustfmt $1/feature_rustfmt + + echo -e "\nRuntime dependencies for rustfmt -- LD_LIBRARY_PATH: $LD_LIBRARY_PATH" + RUSFMT_BIN=$1/rustfmt + RUSTFMT_VERSION=$($RUSFMT_BIN --version) + echo -e "\nRUSFMT_BIN $RUSTFMT_VERSION\n" + FEATURE_BIN=$1/feature_rustfmt + FEATURE_VERSION=$($FEATURE_BIN --version) + echo -e "FEATURE_BIN $FEATURE_VERSION\n" } # Check the diff for running rustfmt and the feature branch on all the .rs files in the repo. @@ -155,7 +179,7 @@ function check_repo() { STATUSES+=($?) set -e - echo "removing tmp_dir $tmp_dir" + echo -e "removing tmp_dir $tmp_dir\n\n" rm -rf $tmp_dir cd $WORKDIR } diff --git a/rust-toolchain b/rust-toolchain index 0057e2f370a6..719b5eabf892 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-10-22" +channel = "nightly-2023-12-12" components = ["llvm-tools", "rustc-dev"] diff --git a/src/comment.rs b/src/comment.rs index f7cd7cefb3d6..44b3eb80aef1 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -173,10 +173,7 @@ pub(crate) fn combine_strs_with_missing_comments( ) -> Option { trace!( "combine_strs_with_missing_comments `{}` `{}` {:?} {:?}", - prev_str, - next_str, - span, - shape + prev_str, next_str, span, shape ); let mut result = diff --git a/src/config/config_type.rs b/src/config/config_type.rs index feb452d7235f..7551241fc001 100644 --- a/src/config/config_type.rs +++ b/src/config/config_type.rs @@ -129,6 +129,7 @@ macro_rules! create_config { | "chain_width" => self.0.set_heuristics(), "merge_imports" => self.0.set_merge_imports(), "fn_args_layout" => self.0.set_fn_args_layout(), + "hide_parse_errors" => self.0.set_hide_parse_errors(), &_ => (), } } @@ -184,6 +185,7 @@ macro_rules! create_config { self.set_ignore(dir); self.set_merge_imports(); self.set_fn_args_layout(); + self.set_hide_parse_errors(); self } @@ -278,19 +280,21 @@ macro_rules! create_config { | "chain_width" => self.set_heuristics(), "merge_imports" => self.set_merge_imports(), "fn_args_layout" => self.set_fn_args_layout(), + "hide_parse_errors" => self.set_hide_parse_errors(), &_ => (), } } #[allow(unreachable_pub)] pub fn is_hidden_option(name: &str) -> bool { - const HIDE_OPTIONS: [&str; 6] = [ + const HIDE_OPTIONS: [&str; 7] = [ "verbose", "verbose_diff", "file_lines", "width_heuristics", "merge_imports", - "fn_args_layout" + "fn_args_layout", + "hide_parse_errors" ]; HIDE_OPTIONS.contains(&name) } @@ -461,6 +465,18 @@ macro_rules! create_config { } } + fn set_hide_parse_errors(&mut self) { + if self.was_set().hide_parse_errors() { + eprintln!( + "Warning: the `hide_parse_errors` option is deprecated. \ + Use `show_parse_errors` instead" + ); + if !self.was_set().show_parse_errors() { + self.show_parse_errors.2 = self.hide_parse_errors(); + } + } + } + #[allow(unreachable_pub)] /// Returns `true` if the config key was explicitly set and is the default value. pub fn is_default(&self, key: &str) -> bool { diff --git a/src/config/mod.rs b/src/config/mod.rs index 9d454137b2c8..2b38b616e299 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -26,6 +26,7 @@ pub(crate) mod file_lines; #[allow(unreachable_pub)] pub(crate) mod lists; pub(crate) mod macro_names; +pub(crate) mod style_edition; // This macro defines configuration options used in rustfmt. Each option // is defined as follows: @@ -73,7 +74,7 @@ create_config! { format_strings: bool, false, false, "Format string literals where necessary"; format_macro_matchers: bool, false, false, "Format the metavariable matching patterns in macros"; - format_macro_bodies: bool, true, false, "Format the bodies of macros"; + format_macro_bodies: bool, true, false, "Format the bodies of declarative macro definitions"; skip_macro_invocations: MacroSelectors, MacroSelectors::default(), false, "Skip formatting the bodies of macros invoked with the following names."; hex_literal_case: HexLiteralCase, HexLiteralCase::Preserve, false, @@ -168,7 +169,8 @@ create_config! { "Enables unstable features. Only available on nightly channel"; disable_all_formatting: bool, false, true, "Don't reformat anything"; skip_children: bool, false, false, "Don't reformat out of line modules"; - hide_parse_errors: bool, false, false, "Hide errors from the parser"; + hide_parse_errors: bool, false, false, "(deprecated: use show_parse_errors instead)"; + show_parse_errors: bool, true, false, "Show errors from the parser (unstable)"; error_on_line_overflow: bool, false, false, "Error if unable to get all lines within max_width"; error_on_unformatted: bool, false, false, "Error if unable to get comments or string literals within max_width, \ @@ -203,6 +205,7 @@ impl PartialConfig { cloned.print_misformatted_file_names = None; cloned.merge_imports = None; cloned.fn_args_layout = None; + cloned.hide_parse_errors = None; ::toml::to_string(&cloned).map_err(ToTomlError) } @@ -455,6 +458,13 @@ mod test { fn_params_layout: Density, Density::Tall, true, "Control the layout of parameters in a function signatures."; + // hide_parse_errors renamed to show_parse_errors + hide_parse_errors: bool, false, false, + "(deprecated: use show_parse_errors instead)"; + show_parse_errors: bool, true, false, + "Show errors from the parser (unstable)"; + + // Width Heuristics use_small_heuristics: Heuristics, Heuristics::Default, true, "Whether to use different formatting for items and \ @@ -680,7 +690,7 @@ required_version = "{}" unstable_features = false disable_all_formatting = false skip_children = false -hide_parse_errors = false +show_parse_errors = true error_on_line_overflow = false error_on_unformatted = false ignore = [] diff --git a/src/config/options.rs b/src/config/options.rs index 03fd91eb10f0..e7fd2cfbd31d 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -470,3 +470,27 @@ pub enum MatchArmLeadingPipe { /// Preserve any existing leading pipes Preserve, } + +/// Defines the default values for each config according to [the style guide]. +/// rustfmt output may differ between style editions. +/// +/// [the style guide]: https://doc.rust-lang.org/nightly/style-guide/ +#[config_type] +pub enum StyleEdition { + #[value = "2015"] + #[doc_hint = "2015"] + /// [Edition 2015]() + Edition2015, + #[value = "2018"] + #[doc_hint = "2018"] + /// [Edition 2018]() + Edition2018, + #[value = "2021"] + #[doc_hint = "2021"] + /// [Edition 2021]() + Edition2021, + #[value = "2024"] + #[doc_hint = "2024"] + /// [Edition 2024](). + Edition2024, +} diff --git a/src/config/style_edition.rs b/src/config/style_edition.rs new file mode 100644 index 000000000000..4dd5f0164faf --- /dev/null +++ b/src/config/style_edition.rs @@ -0,0 +1,69 @@ +use crate::config::StyleEdition; + +/// Defines the default value for the given style edition +pub(crate) trait StyleEditionDefault { + type ConfigType; + fn style_edition_default(style_edition: StyleEdition) -> Self::ConfigType; +} + +/// macro to help implement `StyleEditionDefault` for config options +#[macro_export] +macro_rules! style_edition_default { + ($ty:ident, $config_ty:ty, _ => $default:expr) => { + impl $crate::config::style_edition::StyleEditionDefault for $ty { + type ConfigType = $config_ty; + + fn style_edition_default(_: $crate::config::StyleEdition) -> Self::ConfigType { + $default + } + } + }; + ($ty:ident, $config_ty:ty, Edition2024 => $default_2024:expr, _ => $default_2015:expr) => { + impl $crate::config::style_edition::StyleEditionDefault for $ty { + type ConfigType = $config_ty; + + fn style_edition_default( + style_edition: $crate::config::StyleEdition, + ) -> Self::ConfigType { + match style_edition { + $crate::config::StyleEdition::Edition2015 + | $crate::config::StyleEdition::Edition2018 + | $crate::config::StyleEdition::Edition2021 => $default_2015, + $crate::config::StyleEdition::Edition2024 => $default_2024, + } + } + } + }; +} + +#[cfg(test)] +mod test { + use super::*; + use crate::config::StyleEdition; + + #[test] + fn test_impl_default_style_edition_struct_for_all_editions() { + struct Unit; + style_edition_default!(Unit, usize, _ => 100); + + // regardless of the style edition used the value will always return 100 + assert_eq!(Unit::style_edition_default(StyleEdition::Edition2015), 100); + assert_eq!(Unit::style_edition_default(StyleEdition::Edition2018), 100); + assert_eq!(Unit::style_edition_default(StyleEdition::Edition2021), 100); + assert_eq!(Unit::style_edition_default(StyleEdition::Edition2024), 100); + } + + #[test] + fn test_impl_default_style_edition_for_old_and_new_editions() { + struct Unit; + style_edition_default!(Unit, usize, Edition2024 => 50, _ => 100); + + // style edition 2015-2021 are all the same + assert_eq!(Unit::style_edition_default(StyleEdition::Edition2015), 100); + assert_eq!(Unit::style_edition_default(StyleEdition::Edition2018), 100); + assert_eq!(Unit::style_edition_default(StyleEdition::Edition2021), 100); + + // style edition 2024 + assert_eq!(Unit::style_edition_default(StyleEdition::Edition2024), 50); + } +} diff --git a/src/items.rs b/src/items.rs index 6fb69d6b8839..0bcfa3f448bb 100644 --- a/src/items.rs +++ b/src/items.rs @@ -832,13 +832,15 @@ pub(crate) fn format_impl( if is_impl_single_line(context, items.as_slice(), &result, &where_clause_str, item)? { result.push_str(&where_clause_str); - if where_clause_str.contains('\n') || last_line_contains_single_line_comment(&result) { - // if the where_clause contains extra comments AND - // there is only one where-clause predicate - // recover the suppressed comma in single line where_clause formatting + if where_clause_str.contains('\n') { + // If there is only one where-clause predicate + // and the where-clause spans multiple lines, + // then recover the suppressed comma in single line where-clause formatting if generics.where_clause.predicates.len() == 1 { result.push(','); } + } + if where_clause_str.contains('\n') || last_line_contains_single_line_comment(&result) { result.push_str(&format!("{sep}{{{sep}}}")); } else { result.push_str(" {}"); diff --git a/src/lib.rs b/src/lib.rs index 877d057a34ba..400d3fe6dce2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ #![warn(unreachable_pub)] #![recursion_limit = "256"] #![allow(clippy::match_like_matches_macro)] -#![allow(unreachable_pub)] #[cfg(test)] #[macro_use] @@ -305,7 +304,7 @@ fn format_snippet(snippet: &str, config: &Config, is_macro_def: bool) -> Option< let mut out: Vec = Vec::with_capacity(snippet.len() * 2); config.set().emit_mode(config::EmitMode::Stdout); config.set().verbose(Verbosity::Quiet); - config.set().hide_parse_errors(true); + config.set().show_parse_errors(false); if is_macro_def { config.set().error_on_unformatted(true); } diff --git a/src/macros.rs b/src/macros.rs index b4c58d2fefb1..86694089fd63 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1269,7 +1269,7 @@ impl MacroBranch { let has_block_body = old_body.starts_with('{'); let mut config = context.config.clone(); - config.set().hide_parse_errors(true); + config.set().show_parse_errors(false); result += " {"; diff --git a/src/matches.rs b/src/matches.rs index 5a00984d4c00..72386f0a0978 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -451,8 +451,8 @@ fn rewrite_match_body( }; let block_sep = match context.config.control_brace_style() { - ControlBraceStyle::AlwaysNextLine => format!("{}{}", alt_block_sep, body_prefix), _ if body_prefix.is_empty() => "".to_owned(), + ControlBraceStyle::AlwaysNextLine => format!("{}{}", alt_block_sep, body_prefix), _ if forbid_same_line || !arrow_comment.is_empty() => { format!("{}{}", alt_block_sep, body_prefix) } diff --git a/src/overflow.rs b/src/overflow.rs index f46583b1c899..2f9705ec9434 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -8,8 +8,8 @@ use rustc_ast::{ast, ptr}; use rustc_span::Span; use crate::closures; -use crate::config::lists::*; use crate::config::Version; +use crate::config::{lists::*, Config}; use crate::expr::{ can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr, rewrite_cond, @@ -60,6 +60,13 @@ const SPECIAL_CASE_MACROS: &[(&str, usize)] = &[ ("debug_assert_ne!", 2), ]; +/// Additional special case macros for version 2; these are separated to avoid breaking changes in +/// version 1. +const SPECIAL_CASE_MACROS_V2: &[(&str, usize)] = &[ + // From the `log` crate. + ("trace!", 0), +]; + const SPECIAL_CASE_ATTR: &[(&str, usize)] = &[ // From the `failure` crate. ("fail", 0), @@ -182,12 +189,17 @@ impl<'a> OverflowableItem<'a> { } } - fn special_cases(&self) -> &'static [(&'static str, usize)] { - match self { + fn special_cases(&self, config: &Config) -> impl Iterator { + let base_cases = match self { OverflowableItem::MacroArg(..) => SPECIAL_CASE_MACROS, OverflowableItem::NestedMetaItem(..) => SPECIAL_CASE_ATTR, _ => &[], - } + }; + let additional_cases = match (self, config.version()) { + (OverflowableItem::MacroArg(..), Version::Two) => SPECIAL_CASE_MACROS_V2, + _ => &[], + }; + base_cases.iter().chain(additional_cases) } } @@ -551,7 +563,7 @@ impl<'a> Context<'a> { if tactic == DefinitiveListTactic::Vertical { if let Some((all_simple, num_args_before)) = - maybe_get_args_offset(self.ident, &self.items) + maybe_get_args_offset(self.ident, &self.items, &self.context.config) { let one_line = all_simple && definitive_tactic( @@ -771,11 +783,11 @@ fn no_long_items(list: &[ListItem], short_array_element_width_threshold: usize) pub(crate) fn maybe_get_args_offset( callee_str: &str, args: &[OverflowableItem<'_>], + config: &Config, ) -> Option<(bool, usize)> { if let Some(&(_, num_args_before)) = args .get(0)? - .special_cases() - .iter() + .special_cases(config) .find(|&&(s, _)| s == callee_str) { let all_simple = args.len() > num_args_before diff --git a/src/parse/session.rs b/src/parse/session.rs index 8303c03e1eb4..646d2d132a8f 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -122,7 +122,7 @@ fn default_dcx( source_map: Lrc, ignore_path_set: Lrc, can_reset: Lrc, - hide_parse_errors: bool, + show_parse_errors: bool, color: Color, ) -> DiagCtxt { let supports_color = term::stderr().map_or(false, |term| term.supports_color()); @@ -132,7 +132,7 @@ fn default_dcx( ColorConfig::Never }; - let emitter = if hide_parse_errors { + let emitter = if !show_parse_errors { silent_emitter() } else { let fallback_bundle = rustc_errors::fallback_fluent_bundle( @@ -163,7 +163,7 @@ impl ParseSess { Lrc::clone(&source_map), Lrc::clone(&ignore_path_set), Lrc::clone(&can_reset_errors), - config.hide_parse_errors(), + config.show_parse_errors(), config.color(), ); let parse_sess = RawParseSess::with_dcx(dcx, source_map); diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs index 7dcf7c8416ee..87b55ca1d1d2 100644 --- a/tests/rustfmt/main.rs +++ b/tests/rustfmt/main.rs @@ -184,3 +184,19 @@ fn dont_emit_ICE() { assert!(!stderr.contains("thread 'main' panicked")); } } + +#[test] +fn rustfmt_emits_error_when_control_brace_style_is_always_next_line() { + // See also https://github.com/rust-lang/rustfmt/issues/5912 + let args = [ + "--config=color=Never", + "--config", + "control_brace_style=AlwaysNextLine", + "--config", + "match_arm_blocks=false", + "tests/target/issue_5912.rs", + ]; + + let (_stdout, stderr) = rustfmt(&args); + assert!(!stderr.contains("error[internal]: left behind trailing whitespace")) +} diff --git a/tests/source/issue-5987/two.rs b/tests/source/issue-5987/two.rs new file mode 100644 index 000000000000..e20026b55653 --- /dev/null +++ b/tests/source/issue-5987/two.rs @@ -0,0 +1,13 @@ +// rustfmt-version: Two + +fn main() { + trace!( + "get some longer length in here yes yes {} {}", + "hello", + "world" + ); + debug!( + "get some longer length in here yes yes {} {}", + "hello", "world" + ); +} diff --git a/tests/source/issue_5912.rs b/tests/source/issue_5912.rs new file mode 100644 index 000000000000..2265fd2146ce --- /dev/null +++ b/tests/source/issue_5912.rs @@ -0,0 +1,15 @@ +// rustfmt-match_arm_blocks: false +// rustfmt-control_brace_style: AlwaysNextLine + +fn foo() { + match 0 { + 0 => { + aaaaaaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbbbbb + + bbbbbbbbbbbbbbbbbbbbbbbbb + + bbbbbbbbbbbbbbbbbbbbbbbbb + + bbbbbbbbbbbbbbbbbbbbbbbbb + } + _ => 2, + } +} diff --git a/tests/target/impl.rs b/tests/target/impl.rs index f37fbcf1fcbc..10de0ecde565 100644 --- a/tests/target/impl.rs +++ b/tests/target/impl.rs @@ -32,6 +32,11 @@ where { } +// #5941 +impl T where (): Clone // Should not add comma to comment +{ +} + // #1823 default impl Trait for X {} default unsafe impl Trait for Y {} diff --git a/tests/target/issue-5987/one.rs b/tests/target/issue-5987/one.rs new file mode 100644 index 000000000000..3c995ed28bad --- /dev/null +++ b/tests/target/issue-5987/one.rs @@ -0,0 +1,13 @@ +// rustfmt-version: One + +fn main() { + trace!( + "get some longer length in here yes yes {} {}", + "hello", + "world" + ); + debug!( + "get some longer length in here yes yes {} {}", + "hello", "world" + ); +} diff --git a/tests/target/issue-5987/two.rs b/tests/target/issue-5987/two.rs new file mode 100644 index 000000000000..8fd92fc179e2 --- /dev/null +++ b/tests/target/issue-5987/two.rs @@ -0,0 +1,12 @@ +// rustfmt-version: Two + +fn main() { + trace!( + "get some longer length in here yes yes {} {}", + "hello", "world" + ); + debug!( + "get some longer length in here yes yes {} {}", + "hello", "world" + ); +} diff --git a/tests/target/issue_5912.rs b/tests/target/issue_5912.rs new file mode 100644 index 000000000000..835f2aba971c --- /dev/null +++ b/tests/target/issue_5912.rs @@ -0,0 +1,15 @@ +// rustfmt-match_arm_blocks: false +// rustfmt-control_brace_style: AlwaysNextLine + +fn foo() { + match 0 + { + 0 => + aaaaaaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbbbbb + + bbbbbbbbbbbbbbbbbbbbbbbbb + + bbbbbbbbbbbbbbbbbbbbbbbbb + + bbbbbbbbbbbbbbbbbbbbbbbbb, + _ => 2, + } +}