diff options
| author | Caleb Cartwright <caleb.cartwright@outlook.com> | 2023-10-22 12:45:06 -0500 |
|---|---|---|
| committer | Caleb Cartwright <caleb.cartwright@outlook.com> | 2023-10-22 12:45:06 -0500 |
| commit | f35f25287fea7d45cb05764e5d8b0a3a4e4ab2cd (patch) | |
| tree | bd7f33be19074b7fe3bfe4431645078cb73b1951 /src | |
| parent | 4b5ef37e217d98de6ea3cef71aea9f47c2e9ce23 (diff) | |
| parent | 547577fa5d309d90292ca3a58fef1bf0d9325cc0 (diff) | |
| download | rust-f35f25287fea7d45cb05764e5d8b0a3a4e4ab2cd.tar.gz rust-f35f25287fea7d45cb05764e5d8b0a3a4e4ab2cd.zip | |
Merge remote-tracking branch 'upstream/master' into subtree-sync-2023-10-22
Diffstat (limited to 'src')
40 files changed, 725 insertions, 504 deletions
diff --git a/src/attr.rs b/src/attr.rs index 22e45082a9f..4d83547d664 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -308,7 +308,7 @@ impl Rewrite for ast::MetaItem { // See #2479 for example. let value = rewrite_literal(context, lit.as_token_lit(), lit.span, lit_shape) .unwrap_or_else(|| context.snippet(lit.span).to_owned()); - format!("{} = {}", path, value) + format!("{path} = {value}") } }) } @@ -342,7 +342,7 @@ impl Rewrite for ast::Attribute { let literal_str = literal.as_str(); let doc_comment_formatter = DocCommentFormatter::new(literal_str, comment_style); - let doc_comment = format!("{}", doc_comment_formatter); + let doc_comment = format!("{doc_comment_formatter}"); return rewrite_doc_comment( &doc_comment, shape.comment(context.config), @@ -406,9 +406,9 @@ impl Rewrite for [ast::Attribute] { 0, )?; let comment = if comment.is_empty() { - format!("\n{}", mlb) + format!("\n{mlb}") } else { - format!("{}{}\n{}", mla, comment, mlb) + format!("{mla}{comment}\n{mlb}") }; result.push_str(&comment); result.push_str(&shape.indent.to_string(context.config)); diff --git a/src/attr/doc_comment.rs b/src/attr/doc_comment.rs index 25c8158df8c..f55201839b5 100644 --- a/src/attr/doc_comment.rs +++ b/src/attr/doc_comment.rs @@ -20,15 +20,15 @@ impl Display for DocCommentFormatter<'_> { // Handle `#[doc = ""]`. if lines.peek().is_none() { - return write!(formatter, "{}", opener); + return write!(formatter, "{opener}"); } while let Some(line) = lines.next() { let is_last_line = lines.peek().is_none(); if is_last_line { - write!(formatter, "{}{}", opener, line)?; + write!(formatter, "{opener}{line}")?; } else { - writeln!(formatter, "{}{}", opener, line)?; + writeln!(formatter, "{opener}{line}")?; } } Ok(()) diff --git a/src/bin/main.rs b/src/bin/main.rs index 03b75c1b041..6f564083656 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -6,6 +6,7 @@ use io::Error as IoError; use thiserror::Error; use rustfmt_nightly as rustfmt; +use tracing_subscriber::EnvFilter; use std::collections::HashMap; use std::env; @@ -29,13 +30,15 @@ extern crate rustc_driver; fn main() { rustc_driver::install_ice_hook(BUG_REPORT_URL, |_| ()); - env_logger::Builder::from_env("RUSTFMT_LOG").init(); + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_env("RUSTFMT_LOG")) + .init(); let opts = make_opts(); let exit_code = match execute(&opts) { Ok(code) => code, Err(e) => { - eprintln!("{:#}", e); + eprintln!("{e:#}"); 1 } }; @@ -281,7 +284,7 @@ fn format_string(input: String, options: GetOptsOptions) -> Result<i32> { for f in config.file_lines().files() { match *f { FileName::Stdin => {} - _ => eprintln!("Warning: Extra file listed in file_lines option '{}'", f), + _ => eprintln!("Warning: Extra file listed in file_lines option '{f}'"), } } @@ -377,7 +380,7 @@ fn format_and_emit_report<T: Write>(session: &mut Session<'_, T>, input: Input) } } Err(msg) => { - eprintln!("Error writing files: {}", msg); + eprintln!("Error writing files: {msg}"); session.add_operational_error(); } } @@ -400,12 +403,9 @@ fn print_usage_to_stdout(opts: &Options, reason: &str) { let sep = if reason.is_empty() { String::new() } else { - format!("{}\n\n", reason) + format!("{reason}\n\n") }; - let msg = format!( - "{}Format Rust code\n\nusage: rustfmt [options] <file>...", - sep - ); + let msg = format!("{sep}Format Rust code\n\nusage: rustfmt [options] <file>..."); println!("{}", opts.usage(&msg)); } @@ -419,7 +419,7 @@ are 1-based and inclusive of both end points. Specifying an empty array will result in no files being formatted. For example, ``` -rustfmt --file-lines '[ +rustfmt src/lib.rs src/foo.rs --file-lines '[ {{\"file\":\"src/lib.rs\",\"range\":[7,13]}}, {{\"file\":\"src/lib.rs\",\"range\":[21,29]}}, {{\"file\":\"src/foo.rs\",\"range\":[10,11]}}, @@ -439,7 +439,7 @@ fn print_version() { include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt")) ); - println!("rustfmt {}", version_info); + println!("rustfmt {version_info}"); } fn determine_operation(matches: &Matches) -> Result<Operation, OperationError> { @@ -644,9 +644,9 @@ impl GetOptsOptions { match *f { FileName::Real(ref f) if files.contains(f) => {} FileName::Real(_) => { - eprintln!("Warning: Extra file listed in file_lines option '{}'", f) + eprintln!("Warning: Extra file listed in file_lines option '{f}'") } - FileName::Stdin => eprintln!("Warning: Not a file '{}'", f), + FileName::Stdin => eprintln!("Warning: Not a file '{f}'"), } } } diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index bc9745275f2..a1ad1aafac4 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -22,27 +22,28 @@ use clap::{CommandFactory, Parser}; mod cargo_fmt_tests; #[derive(Parser)] -#[clap( +#[command( disable_version_flag = true, bin_name = "cargo fmt", about = "This utility formats all bin and lib files of \ the current crate using rustfmt." )] +#[command(styles = clap_cargo::style::CLAP_STYLING)] pub struct Opts { /// No output printed to stdout - #[clap(short = 'q', long = "quiet")] + #[arg(short = 'q', long = "quiet")] quiet: bool, /// Use verbose output - #[clap(short = 'v', long = "verbose")] + #[arg(short = 'v', long = "verbose")] verbose: bool, /// Print rustfmt version and exit - #[clap(long = "version")] + #[arg(long = "version")] version: bool, /// Specify package to format - #[clap( + #[arg( short = 'p', long = "package", value_name = "package", @@ -51,24 +52,24 @@ pub struct Opts { packages: Vec<String>, /// Specify path to Cargo.toml - #[clap(long = "manifest-path", value_name = "manifest-path")] + #[arg(long = "manifest-path", value_name = "manifest-path")] manifest_path: Option<String>, /// Specify message-format: short|json|human - #[clap(long = "message-format", value_name = "message-format")] + #[arg(long = "message-format", value_name = "message-format")] message_format: Option<String>, /// Options passed to rustfmt // 'raw = true' to make `--` explicit. - #[clap(name = "rustfmt_options", raw(true))] + #[arg(name = "rustfmt_options", raw = true)] rustfmt_options: Vec<String>, /// Format all packages, and also their local path-based dependencies - #[clap(long = "all")] + #[arg(long = "all")] format_all: bool, /// Run rustfmt in check mode - #[clap(long = "check")] + #[arg(long = "check")] check: bool, } @@ -200,14 +201,13 @@ fn convert_message_format_to_rustfmt_args( } "human" => Ok(()), _ => Err(format!( - "invalid --message-format value: {}. Allowed values are: short|json|human", - message_format + "invalid --message-format value: {message_format}. Allowed values are: short|json|human" )), } } fn print_usage_to_stderr(reason: &str) { - eprintln!("{}", reason); + eprintln!("{reason}"); let app = Opts::command(); app.after_help("") .write_help(&mut io::stderr()) @@ -460,7 +460,7 @@ fn get_targets_with_hitlist( let package = workspace_hitlist.iter().next().unwrap(); Err(io::Error::new( io::ErrorKind::InvalidInput, - format!("package `{}` is not a member of the workspace", package), + format!("package `{package}` is not a member of the workspace"), )) } } @@ -498,7 +498,7 @@ fn run_rustfmt( if verbosity == Verbosity::Verbose { print!("rustfmt"); - print!(" --edition {}", edition); + print!(" --edition {edition}"); fmt_args.iter().for_each(|f| print!(" {}", f)); files.iter().for_each(|f| print!(" {}", f.display())); println!(); diff --git a/src/chains.rs b/src/chains.rs index 0afce7cf659..ea23690caed 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -153,7 +153,13 @@ enum CommentPosition { Top, } -// An expression plus trailing `?`s to be formatted together. +/// Information about an expression in a chain. +struct SubExpr { + expr: ast::Expr, + is_method_call_receiver: bool, +} + +/// An expression plus trailing `?`s to be formatted together. #[derive(Debug)] struct ChainItem { kind: ChainItemKind, @@ -166,7 +172,10 @@ struct ChainItem { // would remove a lot of cloning. #[derive(Debug)] enum ChainItemKind { - Parent(ast::Expr), + Parent { + expr: ast::Expr, + parens: bool, + }, MethodCall( ast::PathSegment, Vec<ast::GenericArg>, @@ -181,7 +190,7 @@ enum ChainItemKind { impl ChainItemKind { fn is_block_like(&self, context: &RewriteContext<'_>, reps: &str) -> bool { match self { - ChainItemKind::Parent(ref expr) => utils::is_block_expr(context, expr, reps), + ChainItemKind::Parent { expr, .. } => utils::is_block_expr(context, expr, reps), ChainItemKind::MethodCall(..) | ChainItemKind::StructField(..) | ChainItemKind::TupleField(..) @@ -199,7 +208,11 @@ impl ChainItemKind { } } - fn from_ast(context: &RewriteContext<'_>, expr: &ast::Expr) -> (ChainItemKind, Span) { + fn from_ast( + context: &RewriteContext<'_>, + expr: &ast::Expr, + is_method_call_receiver: bool, + ) -> (ChainItemKind, Span) { let (kind, span) = match expr.kind { ast::ExprKind::MethodCall(ref call) => { let types = if let Some(ref generic_args) = call.seg.args { @@ -236,7 +249,15 @@ impl ChainItemKind { let span = mk_sp(nested.span.hi(), expr.span.hi()); (ChainItemKind::Await, span) } - _ => return (ChainItemKind::Parent(expr.clone()), expr.span), + _ => { + return ( + ChainItemKind::Parent { + expr: expr.clone(), + parens: is_method_call_receiver && should_add_parens(expr), + }, + expr.span, + ); + } }; // Remove comments from the span. @@ -249,7 +270,14 @@ impl Rewrite for ChainItem { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { let shape = shape.sub_width(self.tries)?; let rewrite = match self.kind { - ChainItemKind::Parent(ref expr) => expr.rewrite(context, shape)?, + ChainItemKind::Parent { + ref expr, + parens: true, + } => crate::expr::rewrite_paren(context, &expr, shape, expr.span)?, + ChainItemKind::Parent { + ref expr, + parens: false, + } => expr.rewrite(context, shape)?, ChainItemKind::MethodCall(ref segment, ref types, ref exprs) => { Self::rewrite_method_call(segment.ident, types, exprs, self.span, context, shape)? } @@ -268,13 +296,14 @@ impl Rewrite for ChainItem { rewrite_comment(comment, false, shape, context.config)? } }; - Some(format!("{}{}", rewrite, "?".repeat(self.tries))) + Some(format!("{rewrite}{}", "?".repeat(self.tries))) } } impl ChainItem { - fn new(context: &RewriteContext<'_>, expr: &ast::Expr, tries: usize) -> ChainItem { - let (kind, span) = ChainItemKind::from_ast(context, expr); + fn new(context: &RewriteContext<'_>, expr: &SubExpr, tries: usize) -> ChainItem { + let (kind, span) = + ChainItemKind::from_ast(context, &expr.expr, expr.is_method_call_receiver); ChainItem { kind, tries, span } } @@ -327,7 +356,7 @@ impl Chain { let mut rev_children = vec![]; let mut sub_tries = 0; for subexpr in &subexpr_list { - match subexpr.kind { + match subexpr.expr.kind { ast::ExprKind::Try(_) => sub_tries += 1, _ => { rev_children.push(ChainItem::new(context, subexpr, sub_tries)); @@ -442,11 +471,14 @@ impl Chain { // Returns a Vec of the prefixes of the chain. // E.g., for input `a.b.c` we return [`a.b.c`, `a.b`, 'a'] - fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec<ast::Expr> { - let mut subexpr_list = vec![expr.clone()]; + fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec<SubExpr> { + let mut subexpr_list = vec![SubExpr { + expr: expr.clone(), + is_method_call_receiver: false, + }]; while let Some(subexpr) = Self::pop_expr_chain(subexpr_list.last().unwrap(), context) { - subexpr_list.push(subexpr.clone()); + subexpr_list.push(subexpr); } subexpr_list @@ -454,12 +486,18 @@ impl Chain { // Returns the expression's subexpression, if it exists. When the subexpr // is a try! macro, we'll convert it to shorthand when the option is set. - fn pop_expr_chain(expr: &ast::Expr, context: &RewriteContext<'_>) -> Option<ast::Expr> { - match expr.kind { - ast::ExprKind::MethodCall(ref call) => Some(Self::convert_try(&call.receiver, context)), + fn pop_expr_chain(expr: &SubExpr, context: &RewriteContext<'_>) -> Option<SubExpr> { + match expr.expr.kind { + ast::ExprKind::MethodCall(ref call) => Some(SubExpr { + expr: Self::convert_try(&call.receiver, context), + is_method_call_receiver: true, + }), ast::ExprKind::Field(ref subexpr, _) | ast::ExprKind::Try(ref subexpr) - | ast::ExprKind::Await(ref subexpr, _) => Some(Self::convert_try(subexpr, context)), + | ast::ExprKind::Await(ref subexpr, _) => Some(SubExpr { + expr: Self::convert_try(subexpr, context), + is_method_call_receiver: false, + }), _ => None, } } @@ -940,3 +978,22 @@ fn trim_tries(s: &str) -> String { } result } + +/// Whether a method call's receiver needs parenthesis, like +/// ```rust,ignore +/// || .. .method(); +/// || 1.. .method(); +/// 1. .method(); +/// ``` +/// Which all need parenthesis or a space before `.method()`. +fn should_add_parens(expr: &ast::Expr) -> bool { + match expr.kind { + ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit), + ast::ExprKind::Closure(ref cl) => match cl.body.kind { + ast::ExprKind::Range(_, _, ast::RangeLimits::HalfOpen) => true, + ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit), + _ => false, + }, + _ => false, + } +} diff --git a/src/closures.rs b/src/closures.rs index c95e9a97b43..a09146e9592 100644 --- a/src/closures.rs +++ b/src/closures.rs @@ -12,7 +12,7 @@ use crate::overflow::OverflowableItem; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::Shape; use crate::source_map::SpanUtils; -use crate::types::rewrite_lifetime_param; +use crate::types::rewrite_bound_params; use crate::utils::{last_line_width, left_most_sub_expr, stmt_expr, NodeIdExt}; // This module is pretty messy because of the rules around closures and blocks: @@ -175,7 +175,7 @@ fn rewrite_closure_with_block( shape, false, )?; - Some(format!("{} {}", prefix, block)) + Some(format!("{prefix} {block}")) } // Rewrite closure with a single expression without wrapping its body with block. @@ -246,7 +246,7 @@ fn rewrite_closure_fn_decl( "for<> ".to_owned() } ast::ClosureBinder::For { generic_params, .. } => { - let lifetime_str = rewrite_lifetime_param(context, shape, generic_params)?; + let lifetime_str = rewrite_bound_params(context, shape, generic_params)?; format!("for<{lifetime_str}> ") } ast::ClosureBinder::NotPresent => "".to_owned(), @@ -310,10 +310,7 @@ fn rewrite_closure_fn_decl( .tactic(tactic) .preserve_newline(true); let list_str = write_list(&item_vec, &fmt)?; - let mut prefix = format!( - "{}{}{}{}{}|{}|", - binder, const_, immovable, is_async, mover, list_str - ); + let mut prefix = format!("{binder}{const_}{immovable}{is_async}{mover}|{list_str}|"); if !ret_str.is_empty() { if prefix.contains('\n') { diff --git a/src/comment.rs b/src/comment.rs index 85918ecc116..f7957321464 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -58,25 +58,23 @@ fn custom_opener(s: &str) -> &str { } impl<'a> CommentStyle<'a> { - /// Returns `true` if the commenting style covers a line only. + /// Returns `true` if the commenting style cannot span multiple lines. pub(crate) fn is_line_comment(&self) -> bool { - match *self { + matches!( + self, CommentStyle::DoubleSlash - | CommentStyle::TripleSlash - | CommentStyle::Doc - | CommentStyle::Custom(_) => true, - _ => false, - } + | CommentStyle::TripleSlash + | CommentStyle::Doc + | CommentStyle::Custom(_) + ) } - /// Returns `true` if the commenting style can span over multiple lines. + /// Returns `true` if the commenting style can span multiple lines. pub(crate) fn is_block_comment(&self) -> bool { - match *self { - CommentStyle::SingleBullet | CommentStyle::DoubleBullet | CommentStyle::Exclamation => { - true - } - _ => false, - } + matches!( + self, + CommentStyle::SingleBullet | CommentStyle::DoubleBullet | CommentStyle::Exclamation + ) } /// Returns `true` if the commenting style is for documentation. @@ -367,7 +365,11 @@ fn identify_comment( trim_left_preserve_layout(first_group, shape.indent, config)? } else if !config.normalize_comments() && !config.wrap_comments() - && !config.format_code_in_doc_comments() + && !( + // `format_code_in_doc_comments` should only take effect on doc comments, + // so we only consider it when this comment block is a doc comment block. + is_doc_comment && config.format_code_in_doc_comments() + ) { light_rewrite_comment(first_group, shape.indent, config, is_doc_comment) } else { @@ -484,7 +486,9 @@ impl ItemizedBlock { // allowed. for suffix in [". ", ") "] { if let Some((prefix, _)) = trimmed.split_once(suffix) { - if prefix.len() <= 2 && prefix.chars().all(|c| char::is_ascii_digit(&c)) { + let has_leading_digits = (1..=2).contains(&prefix.len()) + && prefix.chars().all(|c| char::is_ascii_digit(&c)); + if has_leading_digits { return Some(prefix.len() + suffix.len()); } } @@ -623,7 +627,7 @@ impl<'a> CommentRewrite<'a> { is_prev_line_multi_line: false, code_block_attr: None, item_block: None, - comment_line_separator: format!("{}{}", indent_str, line_start), + comment_line_separator: format!("{indent_str}{line_start}"), max_width, indent_str, fmt_indent: shape.indent, @@ -953,7 +957,7 @@ const RUSTFMT_CUSTOM_COMMENT_PREFIX: &str = "//#### "; fn hide_sharp_behind_comment(s: &str) -> Cow<'_, str> { let s_trimmed = s.trim(); if s_trimmed.starts_with("# ") || s_trimmed == "#" { - Cow::from(format!("{}{}", RUSTFMT_CUSTOM_COMMENT_PREFIX, s)) + Cow::from(format!("{RUSTFMT_CUSTOM_COMMENT_PREFIX}{s}")) } else { Cow::from(s) } @@ -1037,7 +1041,7 @@ pub(crate) fn recover_missing_comment_in_span( } else { Cow::from(" ") }; - Some(format!("{}{}", sep, missing_comment)) + Some(format!("{sep}{missing_comment}")) } } @@ -1834,8 +1838,7 @@ fn remove_comment_header(comment: &str) -> &str { } else { assert!( comment.starts_with("/*"), - "string '{}' is not a comment", - comment + "string '{comment}' is not a comment" ); &comment[2..comment.len() - 2] } @@ -2071,6 +2074,7 @@ fn main() { expected_line_start: &str, ) { let block = ItemizedBlock::new(test_input).unwrap(); +<<<<<<< HEAD assert_eq!(1, block.lines.len(), "test_input: {:?}", test_input); assert_eq!( expected_line, &block.lines[0], @@ -2091,6 +2095,15 @@ fn main() { expected_line_start, &block.line_start, "test_input: {:?}", test_input +======= + assert_eq!(1, block.lines.len(), "test_input: {test_input:?}"); + assert_eq!(expected_line, &block.lines[0], "test_input: {test_input:?}"); + assert_eq!(expected_indent, block.indent, "test_input: {test_input:?}"); + assert_eq!(expected_opener, &block.opener, "test_input: {test_input:?}"); + assert_eq!( + expected_line_start, &block.line_start, + "test_input: {test_input:?}" +>>>>>>> upstream/master ); } @@ -2142,13 +2155,23 @@ fn main() { // https://spec.commonmark.org/0.30 says: "A start number may not be negative": "-1. Not a list item.", "-1 Not a list item.", +<<<<<<< HEAD +======= + // Marker without prefix are not recognized as item markers: + ". Not a list item.", + ") Not a list item.", +>>>>>>> upstream/master ]; for line in test_inputs.iter() { let maybe_block = ItemizedBlock::new(line); assert!( maybe_block.is_none(), +<<<<<<< HEAD "The following line shouldn't be classified as a list item: {}", line +======= + "The following line shouldn't be classified as a list item: {line}" +>>>>>>> upstream/master ); } } diff --git a/src/config/config_type.rs b/src/config/config_type.rs index c836b4bbb78..feb452d7235 100644 --- a/src/config/config_type.rs +++ b/src/config/config_type.rs @@ -500,18 +500,16 @@ where // Stable with an unstable option (false, false, _) => { eprintln!( - "Warning: can't set `{} = {:?}`, unstable features are only \ - available in nightly channel.", - option_name, option_value + "Warning: can't set `{option_name} = {option_value:?}`, unstable features are only \ + available in nightly channel." ); 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 + "Warning: can't set `{option_name} = {option_value:?}`, unstable variants are only \ + available in nightly channel." ); false } diff --git a/src/config/file_lines.rs b/src/config/file_lines.rs index e4e51a3f3b4..e33fe9bb283 100644 --- a/src/config/file_lines.rs +++ b/src/config/file_lines.rs @@ -162,7 +162,7 @@ impl fmt::Display for FileLines { None => write!(f, "None")?, Some(map) => { for (file_name, ranges) in map.iter() { - write!(f, "{}: ", file_name)?; + write!(f, "{file_name}: ")?; write!(f, "{}\n", ranges.iter().format(", "))?; } } diff --git a/src/config/macro_names.rs b/src/config/macro_names.rs index 26ad78d6dca..edfe925c2b3 100644 --- a/src/config/macro_names.rs +++ b/src/config/macro_names.rs @@ -3,7 +3,7 @@ use itertools::Itertools; use std::{fmt, str}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use serde_json as json; use thiserror::Error; @@ -30,12 +30,22 @@ impl From<MacroName> for String { } /// Defines a selector to match against a macro. -#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize)] pub enum MacroSelector { Name(MacroName), All, } +impl<'de> Deserialize<'de> for MacroSelector { + fn deserialize<D>(de: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + let s = String::deserialize(de)?; + std::str::FromStr::from_str(&s).map_err(serde::de::Error::custom) + } +} + impl fmt::Display for MacroSelector { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -113,6 +123,6 @@ mod test { #[test] fn macro_names_display() { let macro_names = MacroSelectors::from_str(r#"["foo", "*", "bar"]"#).unwrap(); - assert_eq!(format!("{}", macro_names), "foo, *, bar"); + assert_eq!(format!("{macro_names}"), "foo, *, bar"); } } diff --git a/src/config/mod.rs b/src/config/mod.rs index 6f41b299e87..7538b26522d 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -216,8 +216,8 @@ impl Config { let required_version = self.required_version(); if version != required_version { println!( - "Error: rustfmt version ({}) doesn't match the required version ({})", - version, required_version, + "Error: rustfmt version ({version}) doesn't match the required version \ +({required_version})" ); return false; } @@ -310,20 +310,20 @@ impl Config { .ok_or_else(|| String::from("Parsed config was not table"))?; for key in table.keys() { if !Config::is_valid_name(key) { - let msg = &format!("Warning: Unknown configuration option `{}`\n", key); + let msg = &format!("Warning: Unknown configuration option `{key}`\n"); err.push_str(msg) } } match parsed.try_into() { Ok(parsed_config) => { if !err.is_empty() { - eprint!("{}", err); + eprint!("{err}"); } Ok(Config::default().fill_from_parsed_config(parsed_config, dir)) } Err(e) => { err.push_str("Error: Decoding config file failed:\n"); - err.push_str(format!("{}\n", e).as_str()); + err.push_str(format!("{e}\n").as_str()); err.push_str("Please check your config file."); Err(err) } @@ -563,10 +563,7 @@ mod test { let toml = used_options.to_toml().unwrap(); assert_eq!( toml, - format!( - "merge_derives = {}\nskip_children = {}\n", - merge_derives, skip_children, - ) + format!("merge_derives = {merge_derives}\nskip_children = {skip_children}\n",) ); } diff --git a/src/config/options.rs b/src/config/options.rs index 3aa1a4de99d..e37f4027e4a 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -243,7 +243,7 @@ pub struct WidthHeuristics { impl fmt::Display for WidthHeuristics { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) + write!(f, "{self:?}") } } diff --git a/src/emitter.rs b/src/emitter.rs index dc2c99a301e..9c335314d75 100644 --- a/src/emitter.rs +++ b/src/emitter.rs @@ -47,6 +47,6 @@ pub(crate) trait Emitter { fn ensure_real_path(filename: &FileName) -> &Path { match *filename { FileName::Real(ref path) => path, - _ => panic!("cannot format `{}` and emit to files", filename), + _ => panic!("cannot format `{filename}` and emit to files"), } } diff --git a/src/emitter/checkstyle.rs b/src/emitter/checkstyle.rs index 545b259979d..56d6a0ed681 100644 --- a/src/emitter/checkstyle.rs +++ b/src/emitter/checkstyle.rs @@ -43,7 +43,7 @@ pub(crate) fn output_checkstyle_file<T>( where T: Write, { - write!(writer, r#"<file name="{}">"#, filename)?; + write!(writer, r#"<file name="{filename}">"#)?; for mismatch in diff { let begin_line = mismatch.line_number; let mut current_line; @@ -82,7 +82,7 @@ mod tests { ); assert_eq!( &writer[..], - format!(r#"<file name="{}"></file>"#, file_name).as_bytes() + format!(r#"<file name="{file_name}"></file>"#).as_bytes() ); } diff --git a/src/emitter/checkstyle/xml.rs b/src/emitter/checkstyle/xml.rs index f251aabe878..d1d9af70857 100644 --- a/src/emitter/checkstyle/xml.rs +++ b/src/emitter/checkstyle/xml.rs @@ -13,7 +13,7 @@ impl<'a> Display for XmlEscaped<'a> { '"' => write!(formatter, """), '\'' => write!(formatter, "'"), '&' => write!(formatter, "&"), - _ => write!(formatter, "{}", char), + _ => write!(formatter, "{char}"), }?; } diff --git a/src/emitter/diff.rs b/src/emitter/diff.rs index 5e1f1344656..764cd136e01 100644 --- a/src/emitter/diff.rs +++ b/src/emitter/diff.rs @@ -28,7 +28,7 @@ impl Emitter for DiffEmitter { if has_diff { if self.config.print_misformatted_file_names() { - writeln!(output, "{}", filename)?; + writeln!(output, "{filename}")?; } else { print_diff( mismatch, @@ -40,7 +40,7 @@ impl Emitter for DiffEmitter { // This occurs when the only difference between the original and formatted values // is the newline style. This happens because The make_diff function compares the // original and formatted values line by line, independent of line endings. - writeln!(output, "Incorrect newline style in {}", filename)?; + writeln!(output, "Incorrect newline style in {filename}")?; return Ok(EmitterResult { has_diff: true }); } @@ -110,7 +110,7 @@ mod tests { assert_eq!( String::from_utf8(writer).unwrap(), - format!("{}\n{}\n", bin_file, lib_file), + format!("{bin_file}\n{lib_file}\n"), ) } diff --git a/src/emitter/json.rs b/src/emitter/json.rs index c7f68d4675a..5594196bed9 100644 --- a/src/emitter/json.rs +++ b/src/emitter/json.rs @@ -96,7 +96,7 @@ impl JsonEmitter { }); } self.mismatched_files.push(MismatchedFile { - name: format!("{}", filename), + name: format!("{filename}"), mismatches, }); Ok(()) @@ -281,7 +281,7 @@ mod tests { }]) .unwrap(); assert_eq!(result.has_diff, true); - assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes()); + assert_eq!(&writer[..], format!("{exp_json}\n").as_bytes()); } #[test] @@ -341,6 +341,6 @@ mod tests { }; let exp_json = to_json_string(&vec![exp_bin, exp_lib]).unwrap(); - assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes()); + assert_eq!(&writer[..], format!("{exp_json}\n").as_bytes()); } } diff --git a/src/emitter/stdout.rs b/src/emitter/stdout.rs index 9fddd515e49..0bbc7332dfe 100644 --- a/src/emitter/stdout.rs +++ b/src/emitter/stdout.rs @@ -24,9 +24,9 @@ impl Emitter for StdoutEmitter { }: FormattedFile<'_>, ) -> Result<EmitterResult, io::Error> { if self.verbosity != Verbosity::Quiet { - writeln!(output, "{}:\n", filename)?; + writeln!(output, "{filename}:\n")?; } - write!(output, "{}", formatted_text)?; + write!(output, "{formatted_text}")?; Ok(EmitterResult::default()) } } diff --git a/src/expr.rs b/src/expr.rs index 03cdddc4140..878e2b7d6d0 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::cmp::min; use itertools::Itertools; -use rustc_ast::token::{Delimiter, LitKind}; +use rustc_ast::token::{Delimiter, Lit, LitKind}; use rustc_ast::{ast, ptr, token}; use rustc_span::{BytePos, Span}; @@ -26,6 +26,7 @@ use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; +use crate::stmt; use crate::string::{rewrite_string, StringFormat}; use crate::types::{rewrite_path, PathContext}; use crate::utils::{ @@ -48,6 +49,10 @@ pub(crate) enum ExprType { SubExpression, } +pub(crate) fn lit_ends_in_dot(lit: &Lit) -> bool { + matches!(lit, Lit { kind: LitKind::Float, suffix: None, symbol } if symbol.as_str().ends_with('.')) +} + pub(crate) fn format_expr( expr: &ast::Expr, expr_type: ExprType, @@ -127,7 +132,7 @@ pub(crate) fn format_expr( ast::ExprKind::Tup(ref items) => { rewrite_tuple(context, items.iter(), expr.span, shape, items.len() == 1) } - ast::ExprKind::Let(..) => None, + ast::ExprKind::Let(ref pat, ref expr, _span) => rewrite_let(context, shape, pat, expr), ast::ExprKind::If(..) | ast::ExprKind::ForLoop(..) | ast::ExprKind::Loop(..) @@ -182,7 +187,7 @@ pub(crate) fn format_expr( Some(label) => format!(" {}", label.ident), None => String::new(), }; - Some(format!("continue{}", id_str)) + Some(format!("continue{id_str}")) } ast::ExprKind::Break(ref opt_label, ref opt_expr) => { let id_str = match *opt_label { @@ -191,9 +196,9 @@ pub(crate) fn format_expr( }; if let Some(ref expr) = *opt_expr { - rewrite_unary_prefix(context, &format!("break{} ", id_str), &**expr, shape) + rewrite_unary_prefix(context, &format!("break{id_str} "), &**expr, shape) } else { - Some(format!("break{}", id_str)) + Some(format!("break{id_str}")) } } ast::ExprKind::Yield(ref opt_expr) => { @@ -275,12 +280,7 @@ pub(crate) fn format_expr( fn needs_space_before_range(context: &RewriteContext<'_>, lhs: &ast::Expr) -> bool { match lhs.kind { - ast::ExprKind::Lit(token_lit) => match token_lit.kind { - token::LitKind::Float if token_lit.suffix.is_none() => { - context.snippet(lhs.span).ends_with('.') - } - _ => false, - }, + ast::ExprKind::Lit(token_lit) => lit_ends_in_dot(&token_lit), ast::ExprKind::Unary(_, ref expr) => needs_space_before_range(context, expr), _ => false, } @@ -309,7 +309,7 @@ pub(crate) fn format_expr( match (lhs.as_ref().map(|x| &**x), rhs.as_ref().map(|x| &**x)) { (Some(lhs), Some(rhs)) => { let sp_delim = if context.config.spaces_around_ranges() { - format!(" {} ", delim) + format!(" {delim} ") } else { default_sp_delim(Some(lhs), Some(rhs)) }; @@ -324,7 +324,7 @@ pub(crate) fn format_expr( } (None, Some(rhs)) => { let sp_delim = if context.config.spaces_around_ranges() { - format!("{} ", delim) + format!("{delim} ") } else { default_sp_delim(None, Some(rhs)) }; @@ -332,7 +332,7 @@ pub(crate) fn format_expr( } (Some(lhs), None) => { let sp_delim = if context.config.spaces_around_ranges() { - format!(" {}", delim) + format!(" {delim}") } else { default_sp_delim(Some(lhs), None) }; @@ -375,7 +375,7 @@ pub(crate) fn format_expr( }; if let rw @ Some(_) = rewrite_single_line_block( context, - format!("{}{}", "async ", mover).as_str(), + format!("async {mover}").as_str(), block, Some(&expr.attrs), None, @@ -386,9 +386,7 @@ pub(crate) fn format_expr( // 6 = `async ` let budget = shape.width.saturating_sub(6); Some(format!( - "{}{}{}", - "async ", - mover, + "async {mover}{}", rewrite_block( block, Some(&expr.attrs), @@ -460,7 +458,7 @@ fn rewrite_empty_block( } if !block_contains_comment(context, block) && shape.width >= 2 { - return Some(format!("{}{}{{}}", prefix, label_str)); + return Some(format!("{prefix}{label_str}{{}}")); } // If a block contains only a single-line comment, then leave it on one line. @@ -473,7 +471,7 @@ fn rewrite_empty_block( && !comment_str.starts_with("//") && comment_str.len() + 4 <= shape.width { - return Some(format!("{}{}{{ {} }}", prefix, label_str, comment_str)); + return Some(format!("{prefix}{label_str}{{ {comment_str} }}")); } } @@ -516,11 +514,11 @@ fn rewrite_single_line_block( label: Option<ast::Label>, shape: Shape, ) -> Option<String> { - if is_simple_block(context, block, attrs) { + if let Some(block_expr) = stmt::Stmt::from_simple_block(context, block, attrs) { let expr_shape = shape.offset_left(last_line_width(prefix))?; - let expr_str = block.stmts[0].rewrite(context, expr_shape)?; + let expr_str = block_expr.rewrite(context, expr_shape)?; let label_str = rewrite_label(label); - let result = format!("{}{}{{ {} }}", prefix, label_str, expr_str); + let result = format!("{prefix}{label_str}{{ {expr_str} }}"); if result.len() <= shape.width && !result.contains('\n') { return Some(result); } @@ -800,19 +798,19 @@ impl<'a> ControlFlow<'a> { let fixed_cost = self.keyword.len() + " { } else { }".len(); if let ast::ExprKind::Block(ref else_node, _) = else_block.kind { - if !is_simple_block(context, self.block, None) - || !is_simple_block(context, else_node, None) - || pat_expr_str.contains('\n') - { - return None; - } + let (if_expr, else_expr) = match ( + stmt::Stmt::from_simple_block(context, self.block, None), + stmt::Stmt::from_simple_block(context, else_node, None), + pat_expr_str.contains('\n'), + ) { + (Some(if_expr), Some(else_expr), false) => (if_expr, else_expr), + _ => return None, + }; let new_width = width.checked_sub(pat_expr_str.len() + fixed_cost)?; - let expr = &self.block.stmts[0]; - let if_str = expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?; + let if_str = if_expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?; let new_width = new_width.checked_sub(if_str.len())?; - let else_expr = &else_node.stmts[0]; let else_str = else_expr.rewrite(context, Shape::legacy(new_width, Indent::empty()))?; if if_str.contains('\n') || else_str.contains('\n') { @@ -1100,7 +1098,7 @@ impl<'a> Rewrite for ControlFlow<'a> { result? }; - let mut result = format!("{}{}", cond_str, block_str); + let mut result = format!("{cond_str}{block_str}"); if let Some(else_block) = self.else_block { let shape = Shape::indented(shape.indent, context.config); @@ -1160,8 +1158,7 @@ fn rewrite_label(opt_label: Option<ast::Label>) -> Cow<'static, str> { fn extract_comment(span: Span, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { match rewrite_missing_comment(span, shape, context) { Some(ref comment) if !comment.is_empty() => Some(format!( - "{indent}{}{indent}", - comment, + "{indent}{comment}{indent}", indent = shape.indent.to_string_with_newline(context.config) )), _ => None, @@ -1436,7 +1433,7 @@ pub(crate) fn span_ends_with_comma(context: &RewriteContext<'_>, span: Span) -> result } -fn rewrite_paren( +pub(crate) fn rewrite_paren( context: &RewriteContext<'_>, mut subexpr: &ast::Expr, shape: Shape, @@ -1452,7 +1449,7 @@ fn rewrite_paren( let remove_nested_parens = context.config.remove_nested_parens(); loop { // 1 = "(" or ")" - pre_span = mk_sp(span.lo() + BytePos(1), subexpr.span.lo()); + pre_span = mk_sp(span.lo() + BytePos(1), subexpr.span().lo()); post_span = mk_sp(subexpr.span.hi(), span.hi() - BytePos(1)); pre_comment = rewrite_missing_comment(pre_span, shape, context)?; post_comment = rewrite_missing_comment(post_span, shape, context)?; @@ -1474,7 +1471,7 @@ fn rewrite_paren( let subexpr_str = subexpr.rewrite(context, sub_shape)?; let fits_single_line = !pre_comment.contains("//") && !post_comment.contains("//"); if fits_single_line { - Some(format!("({}{}{})", pre_comment, subexpr_str, post_comment)) + Some(format!("({pre_comment}{subexpr_str}{post_comment})")) } else { rewrite_paren_in_multi_line(context, subexpr, shape, pre_span, post_span) } @@ -1538,7 +1535,7 @@ fn rewrite_index( // Return if index fits in a single line. match orig_index_rw { Some(ref index_str) if !index_str.contains('\n') => { - return Some(format!("{}[{}]", expr_str, index_str)); + return Some(format!("{expr_str}[{index_str}]")); } _ => (), } @@ -1561,7 +1558,7 @@ fn rewrite_index( indent.to_string_with_newline(context.config), new_index_str, )), - (Some(ref index_str), _) => Some(format!("{}[{}]", expr_str, index_str)), + (Some(ref index_str), _) => Some(format!("{expr_str}[{index_str}]")), _ => None, } } @@ -1593,9 +1590,9 @@ fn rewrite_struct_lit<'a>( let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?; let has_base_or_rest = match struct_rest { - ast::StructRest::None if fields.is_empty() => return Some(format!("{} {{}}", path_str)), + ast::StructRest::None if fields.is_empty() => return Some(format!("{path_str} {{}}")), ast::StructRest::Rest(_) if fields.is_empty() => { - return Some(format!("{} {{ .. }}", path_str)); + return Some(format!("{path_str} {{ .. }}")); } ast::StructRest::Rest(_) | ast::StructRest::Base(_) => true, _ => false, @@ -1686,7 +1683,7 @@ fn rewrite_struct_lit<'a>( let fields_str = wrap_struct_field(context, attrs, &fields_str, shape, v_shape, one_line_width)?; - Some(format!("{} {{{}}}", path_str, fields_str)) + Some(format!("{path_str} {{{fields_str}}}")) // FIXME if context.config.indent_style() == Visual, but we run out // of space, we should fall back to BlockIndent. @@ -1716,7 +1713,7 @@ pub(crate) fn wrap_struct_field( )) } else { // One liner or visual indent. - Some(format!(" {} ", fields_str)) + Some(format!(" {fields_str} ")) } } else { Some(format!( @@ -1765,7 +1762,7 @@ pub(crate) fn rewrite_field( { Some(attrs_str + name) } - Some(e) => Some(format!("{}{}{}{}", attrs_str, name, separator, e)), + Some(e) => Some(format!("{attrs_str}{name}{separator}{e}")), None => { let expr_offset = shape.indent.block_indent(context.config); let expr = field @@ -1830,7 +1827,41 @@ fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>( .ends_with_newline(false); let list_str = write_list(&item_vec, &fmt)?; - Some(format!("({})", list_str)) + Some(format!("({list_str})")) +} + +fn rewrite_let( + context: &RewriteContext<'_>, + shape: Shape, + pat: &ast::Pat, + expr: &ast::Expr, +) -> Option<String> { + let mut result = "let ".to_owned(); + + // TODO(ytmimi) comments could appear between `let` and the `pat` + + // 4 = "let ".len() + let pat_shape = shape.offset_left(4)?; + let pat_str = pat.rewrite(context, pat_shape)?; + result.push_str(&pat_str); + + // TODO(ytmimi) comments could appear between `pat` and `=` + result.push_str(" ="); + + let comments_lo = context + .snippet_provider + .span_after(expr.span.with_lo(pat.span.hi()), "="); + let comments_span = mk_sp(comments_lo, expr.span.lo()); + rewrite_assign_rhs_with_comments( + context, + result, + expr, + shape, + &RhsAssignKind::Expr(&expr.kind, expr.span), + RhsTactics::Default, + comments_span, + true, + ) } pub(crate) fn rewrite_tuple<'a, T: 'a + IntoOverflowableItem<'a>>( @@ -2072,7 +2103,7 @@ fn choose_rhs<R: Rewrite>( Some(ref new_str) if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => { - Some(format!(" {}", new_str)) + Some(format!(" {new_str}")) } _ => { // Expression did not fit on the same line as the identifier. @@ -2089,21 +2120,21 @@ fn choose_rhs<R: Rewrite>( (Some(ref orig_rhs), Some(ref new_rhs)) if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) => { - Some(format!("{}{}", before_space_str, orig_rhs)) + Some(format!("{before_space_str}{orig_rhs}")) } (Some(ref orig_rhs), Some(ref new_rhs)) if prefer_next_line(orig_rhs, new_rhs, rhs_tactics) => { - Some(format!("{}{}", new_indent_str, new_rhs)) + Some(format!("{new_indent_str}{new_rhs}")) } - (None, Some(ref new_rhs)) => Some(format!("{}{}", new_indent_str, new_rhs)), + (None, Some(ref new_rhs)) => Some(format!("{new_indent_str}{new_rhs}")), (None, None) if rhs_tactics == RhsTactics::AllowOverflow => { let shape = shape.infinite_width(); expr.rewrite(context, shape) .map(|s| format!("{}{}", before_space_str, s)) } (None, None) => None, - (Some(orig_rhs), _) => Some(format!("{}{}", before_space_str, orig_rhs)), + (Some(orig_rhs), _) => Some(format!("{before_space_str}{orig_rhs}")), } } } diff --git a/src/format-diff/main.rs b/src/format-diff/main.rs index f6b739e1c2a..61e2cb711a5 100644 --- a/src/format-diff/main.rs +++ b/src/format-diff/main.rs @@ -5,11 +5,12 @@ #![deny(warnings)] #[macro_use] -extern crate log; +extern crate tracing; use serde::{Deserialize, Serialize}; use serde_json as json; use thiserror::Error; +use tracing_subscriber::EnvFilter; use std::collections::HashSet; use std::env; @@ -63,10 +64,12 @@ pub struct Opts { } fn main() { - env_logger::Builder::from_env("RUSTFMT_LOG").init(); + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_env("RUSTFMT_LOG")) + .init(); let opts = Opts::parse(); if let Err(e) = run(opts) { - println!("{}", e); + println!("{e}"); Opts::command() .print_help() .expect("cannot write to stdout"); @@ -110,7 +113,7 @@ fn run_rustfmt(files: &HashSet<String>, ranges: &[Range]) -> Result<(), FormatDi if !exit_status.success() { return Err(FormatDiffError::IoError(io::Error::new( io::ErrorKind::Other, - format!("rustfmt failed with {}", exit_status), + format!("rustfmt failed with {exit_status}"), ))); } Ok(()) @@ -126,12 +129,12 @@ fn scan_diff<R>( where R: io::Read, { - let diff_pattern = format!(r"^\+\+\+\s(?:.*?/){{{}}}(\S*)", skip_prefix); + let diff_pattern = format!(r"^\+\+\+\s(?:.*?/){{{skip_prefix}}}(\S*)"); let diff_pattern = Regex::new(&diff_pattern).unwrap(); let lines_pattern = Regex::new(r"^@@.*\+(\d+)(,(\d+))?").unwrap(); - let file_filter = Regex::new(&format!("^{}$", file_filter))?; + let file_filter = Regex::new(&format!("^{file_filter}$"))?; let mut current_file = None; diff --git a/src/formatting.rs b/src/formatting.rs index 1f4ad6960e2..cd57a025b67 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -296,7 +296,7 @@ impl<'b, T: Write + 'b> FormatHandler for Session<'b, T> { Ok(ref result) if result.has_diff => report.add_diff(), Err(e) => { // Create a new error with path_str to help users see which files failed - let err_msg = format!("{}: {}", path, e); + let err_msg = format!("{path}: {e}"); return Err(io::Error::new(e.kind(), err_msg).into()); } _ => {} diff --git a/src/git-rustfmt/main.rs b/src/git-rustfmt/main.rs index 579778edbe7..3059d917c6b 100644 --- a/src/git-rustfmt/main.rs +++ b/src/git-rustfmt/main.rs @@ -1,5 +1,5 @@ #[macro_use] -extern crate log; +extern crate tracing; use std::env; use std::io::stdout; @@ -9,6 +9,7 @@ use std::str::FromStr; use getopts::{Matches, Options}; use rustfmt_nightly as rustfmt; +use tracing_subscriber::EnvFilter; use crate::rustfmt::{load_config, CliOptions, FormatReportFormatterBuilder, Input, Session}; @@ -42,7 +43,7 @@ fn git_diff(commits: &str) -> String { let mut cmd = Command::new("git"); cmd.arg("diff"); if commits != "0" { - cmd.arg(format!("HEAD~{}", commits)); + cmd.arg(format!("HEAD~{commits}")); } let output = cmd.output().expect("Couldn't execute `git diff`"); String::from_utf8_lossy(&output.stdout).into_owned() @@ -107,7 +108,7 @@ fn check_uncommitted() { if !uncommitted.is_empty() { println!("Found untracked changes:"); for f in &uncommitted { - println!(" {}", f); + println!(" {f}"); } println!("Commit your work, or run with `-u`."); println!("Exiting."); @@ -170,7 +171,9 @@ impl Config { } fn main() { - env_logger::Builder::from_env("RUSTFMT_LOG").init(); + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_env("RUSTFMT_LOG")) + .init(); let opts = make_opts(); let matches = opts diff --git a/src/imports.rs b/src/imports.rs index 339e5cef5af..f8e7fa62890 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -191,7 +191,7 @@ impl UseSegment { "crate" => UseSegmentKind::Crate(None), _ => { let mod_sep = if modsep { "::" } else { "" }; - UseSegmentKind::Ident(format!("{}{}", mod_sep, name), None) + UseSegmentKind::Ident(format!("{mod_sep}{name}"), None) } }; @@ -295,8 +295,8 @@ impl fmt::Display for UseSegmentKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { UseSegmentKind::Glob => write!(f, "*"), - UseSegmentKind::Ident(ref s, Some(ref alias)) => write!(f, "{} as {}", s, alias), - UseSegmentKind::Ident(ref s, None) => write!(f, "{}", s), + UseSegmentKind::Ident(ref s, Some(ref alias)) => write!(f, "{s} as {alias}"), + UseSegmentKind::Ident(ref s, None) => write!(f, "{s}"), UseSegmentKind::Slf(..) => write!(f, "self"), UseSegmentKind::Super(..) => write!(f, "super"), UseSegmentKind::Crate(..) => write!(f, "crate"), @@ -306,7 +306,7 @@ impl fmt::Display for UseSegmentKind { if i != 0 { write!(f, ", ")?; } - write!(f, "{}", item)?; + write!(f, "{item}")?; } write!(f, "}}") } @@ -319,7 +319,7 @@ impl fmt::Display for UseTree { if i != 0 { write!(f, "::")?; } - write!(f, "{}", segment)?; + write!(f, "{segment}")?; } Ok(()) } @@ -589,7 +589,7 @@ impl UseTree { // Normalise foo::{bar} -> foo::bar if let UseSegmentKind::List(ref list) = last.kind { - if list.len() == 1 && list[0].to_string() != "self" { + if list.len() == 1 && list[0].to_string() != "self" && !list[0].has_comment() { normalize_sole_list = true; } } @@ -1032,7 +1032,9 @@ fn rewrite_nested_use_tree( let list_str = write_list(&list_items, &fmt)?; - let result = if (list_str.contains('\n') || list_str.len() > remaining_width) + let result = if (list_str.contains('\n') + || list_str.len() > remaining_width + || tactic == DefinitiveListTactic::Vertical) && context.config.imports_indent() == IndentStyle::Block { format!( @@ -1042,7 +1044,7 @@ fn rewrite_nested_use_tree( shape.indent.to_string(context.config) ) } else { - format!("{{{}}}", list_str) + format!("{{{list_str}}}") }; Some(result) @@ -1052,14 +1054,14 @@ impl Rewrite for UseSegment { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { Some(match self.kind { UseSegmentKind::Ident(ref ident, Some(ref rename)) => { - format!("{} as {}", ident, rename) + format!("{ident} as {rename}") } UseSegmentKind::Ident(ref ident, None) => ident.clone(), - UseSegmentKind::Slf(Some(ref rename)) => format!("self as {}", rename), + UseSegmentKind::Slf(Some(ref rename)) => format!("self as {rename}"), UseSegmentKind::Slf(None) => "self".to_owned(), - UseSegmentKind::Super(Some(ref rename)) => format!("super as {}", rename), + UseSegmentKind::Super(Some(ref rename)) => format!("super as {rename}"), UseSegmentKind::Super(None) => "super".to_owned(), - UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {}", rename), + UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {rename}"), UseSegmentKind::Crate(None) => "crate".to_owned(), UseSegmentKind::Glob => "*".to_owned(), UseSegmentKind::List(ref use_tree_list) => rewrite_nested_use_tree( diff --git a/src/items.rs b/src/items.rs index 002cffa9b0c..edb5a5b629a 100644 --- a/src/items.rs +++ b/src/items.rs @@ -75,6 +75,7 @@ impl Rewrite for ast::Local { false, )? }; + let let_kw_offset = result.len() - "let ".len(); // 4 = "let ".len() let pat_shape = shape.offset_left(4)?; @@ -127,8 +128,15 @@ impl Rewrite for ast::Local { if let Some(block) = else_block { let else_kw_span = init.span.between(block.span); + // Strip attributes and comments to check if newline is needed before the else + // keyword from the initializer part. (#5901) + let init_str = if context.config.version() == Version::Two { + &result[let_kw_offset..] + } else { + result.as_str() + }; let force_newline_else = pat_str.contains('\n') - || !same_line_else_kw_and_brace(&result, context, else_kw_span, nested_shape); + || !same_line_else_kw_and_brace(init_str, context, else_kw_span, nested_shape); let else_kw = rewrite_else_kw_with_comments( force_newline_else, true, @@ -146,11 +154,16 @@ impl Rewrite for ast::Local { std::cmp::min(shape.width, context.config.single_line_let_else_max_width()); // If available_space hits zero we know for sure this will be a multi-lined block - let available_space = max_width.saturating_sub(result.len()); + let assign_str_with_else_kw = if context.config.version() == Version::Two { + &result[let_kw_offset..] + } else { + result.as_str() + }; + let available_space = max_width.saturating_sub(assign_str_with_else_kw.len()); let allow_single_line = !force_newline_else && available_space > 0 - && allow_single_line_let_else_block(&result, block); + && allow_single_line_let_else_block(assign_str_with_else_kw, block); let mut rw_else_block = rewrite_let_else_block(block, allow_single_line, context, shape)?; @@ -248,7 +261,6 @@ impl<'a> Item<'a> { abi: format_extern( ast::Extern::from_abi(fm.abi, DUMMY_SP), config.force_explicit_abi(), - true, ), vis: None, body: fm @@ -306,22 +318,20 @@ impl<'a> FnSig<'a> { defaultness: ast::Defaultness, ) -> FnSig<'a> { match *fn_kind { - visit::FnKind::Fn(fn_ctxt, _, fn_sig, vis, generics, _) => match fn_ctxt { - visit::FnCtxt::Assoc(..) => { - let mut fn_sig = FnSig::from_method_sig(fn_sig, generics, vis); - fn_sig.defaultness = defaultness; - fn_sig - } - _ => FnSig { - decl, - generics, - ext: fn_sig.header.ext, - constness: fn_sig.header.constness, - is_async: Cow::Borrowed(&fn_sig.header.asyncness), - defaultness, - unsafety: fn_sig.header.unsafety, - visibility: vis, - }, + visit::FnKind::Fn(visit::FnCtxt::Assoc(..), _, fn_sig, vis, generics, _) => { + let mut fn_sig = FnSig::from_method_sig(fn_sig, generics, vis); + fn_sig.defaultness = defaultness; + fn_sig + } + visit::FnKind::Fn(_, _, fn_sig, vis, generics, _) => FnSig { + decl, + generics, + ext: fn_sig.header.ext, + constness: fn_sig.header.constness, + is_async: Cow::Borrowed(&fn_sig.header.asyncness), + defaultness, + unsafety: fn_sig.header.unsafety, + visibility: vis, }, _ => unreachable!(), } @@ -338,7 +348,6 @@ impl<'a> FnSig<'a> { result.push_str(&format_extern( self.ext, context.config.force_explicit_abi(), - false, )); result } @@ -472,7 +481,7 @@ impl<'a> FmtVisitor<'a> { && self.block_indent.width() + fn_str.len() + 3 <= self.config.max_width() && !last_line_contains_single_line_comment(fn_str) { - return Some(format!("{} {{}}", fn_str)); + return Some(format!("{fn_str} {{}}")); } if !self.config.fn_single_line() || !is_simple_block_stmt(&context, block, None) { @@ -484,7 +493,7 @@ impl<'a> FmtVisitor<'a> { let width = self.block_indent.width() + fn_str.len() + res.len() + 5; if !res.contains('\n') && width <= self.config.max_width() { - Some(format!("{} {{ {} }}", fn_str, res)) + Some(format!("{fn_str} {{ {res} }}")) } else { None } @@ -666,7 +675,7 @@ impl<'a> FmtVisitor<'a> { }; let variant_body = if let Some(ref expr) = field.disr_expr { - let lhs = format!("{:1$} =", variant_body, pad_discrim_ident_to); + let lhs = format!("{variant_body:pad_discrim_ident_to$} ="); let ex = &*expr.value; rewrite_assign_rhs_with( &context, @@ -829,7 +838,7 @@ pub(crate) fn format_impl( if generics.where_clause.predicates.len() == 1 { result.push(','); } - result.push_str(&format!("{}{{{}}}", sep, sep)); + result.push_str(&format!("{sep}{{{sep}}}")); } else { result.push_str(" {}"); } @@ -1020,7 +1029,7 @@ fn rewrite_trait_ref( let shape = Shape::indented(offset + used_space, context.config); if let Some(trait_ref_str) = trait_ref.rewrite(context, shape) { if !trait_ref_str.contains('\n') { - return Some(format!(" {}{}", polarity_str, trait_ref_str)); + return Some(format!(" {polarity_str}{trait_ref_str}")); } } // We could not make enough space for trait_ref, so put it on new line. @@ -1118,172 +1127,172 @@ pub(crate) fn format_trait( item: &ast::Item, offset: Indent, ) -> Option<String> { - if let ast::ItemKind::Trait(trait_kind) = &item.kind { - let ast::Trait { - is_auto, - unsafety, - ref generics, - ref bounds, - ref items, - } = **trait_kind; - let mut result = String::with_capacity(128); - let header = format!( - "{}{}{}trait ", - format_visibility(context, &item.vis), - format_unsafety(unsafety), - format_auto(is_auto), - ); - result.push_str(&header); + let ast::ItemKind::Trait(trait_kind) = &item.kind else { + unreachable!(); + }; + let ast::Trait { + is_auto, + unsafety, + ref generics, + ref bounds, + ref items, + } = **trait_kind; - let body_lo = context.snippet_provider.span_after(item.span, "{"); + let mut result = String::with_capacity(128); + let header = format!( + "{}{}{}trait ", + format_visibility(context, &item.vis), + format_unsafety(unsafety), + format_auto(is_auto), + ); + result.push_str(&header); - let shape = Shape::indented(offset, context.config).offset_left(result.len())?; - let generics_str = - rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?; - result.push_str(&generics_str); + let body_lo = context.snippet_provider.span_after(item.span, "{"); - // FIXME(#2055): rustfmt fails to format when there are comments between trait bounds. - if !bounds.is_empty() { - let ident_hi = context - .snippet_provider - .span_after(item.span, item.ident.as_str()); - let bound_hi = bounds.last().unwrap().span().hi(); - let snippet = context.snippet(mk_sp(ident_hi, bound_hi)); - if contains_comment(snippet) { - return None; - } + let shape = Shape::indented(offset, context.config).offset_left(result.len())?; + let generics_str = + rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?; + result.push_str(&generics_str); - result = rewrite_assign_rhs_with( - context, - result + ":", - bounds, - shape, - &RhsAssignKind::Bounds, - RhsTactics::ForceNextLineWithoutIndent, - )?; + // FIXME(#2055): rustfmt fails to format when there are comments between trait bounds. + if !bounds.is_empty() { + let ident_hi = context + .snippet_provider + .span_after(item.span, item.ident.as_str()); + let bound_hi = bounds.last().unwrap().span().hi(); + let snippet = context.snippet(mk_sp(ident_hi, bound_hi)); + if contains_comment(snippet) { + return None; } - // Rewrite where-clause. - if !generics.where_clause.predicates.is_empty() { - let where_on_new_line = context.config.indent_style() != IndentStyle::Block; + result = rewrite_assign_rhs_with( + context, + result + ":", + bounds, + shape, + &RhsAssignKind::Bounds, + RhsTactics::ForceNextLineWithoutIndent, + )?; + } + + // Rewrite where-clause. + if !generics.where_clause.predicates.is_empty() { + let where_on_new_line = context.config.indent_style() != IndentStyle::Block; - let where_budget = context.budget(last_line_width(&result)); - let pos_before_where = if bounds.is_empty() { - generics.where_clause.span.lo() + let where_budget = context.budget(last_line_width(&result)); + let pos_before_where = if bounds.is_empty() { + generics.where_clause.span.lo() + } else { + bounds[bounds.len() - 1].span().hi() + }; + let option = WhereClauseOption::snuggled(&generics_str); + let where_clause_str = rewrite_where_clause( + context, + &generics.where_clause.predicates, + generics.where_clause.span, + context.config.brace_style(), + Shape::legacy(where_budget, offset.block_only()), + where_on_new_line, + "{", + None, + pos_before_where, + option, + )?; + // If the where-clause cannot fit on the same line, + // put the where-clause on a new line + if !where_clause_str.contains('\n') + && last_line_width(&result) + where_clause_str.len() + offset.width() + > context.config.comment_width() + { + let width = offset.block_indent + context.config.tab_spaces() - 1; + let where_indent = Indent::new(0, width); + result.push_str(&where_indent.to_string_with_newline(context.config)); + } + result.push_str(&where_clause_str); + } else { + let item_snippet = context.snippet(item.span); + if let Some(lo) = item_snippet.find('/') { + // 1 = `{` + let comment_hi = if generics.params.len() > 0 { + generics.span.lo() - BytePos(1) } else { - bounds[bounds.len() - 1].span().hi() + body_lo - BytePos(1) }; - let option = WhereClauseOption::snuggled(&generics_str); - let where_clause_str = rewrite_where_clause( - context, - &generics.where_clause.predicates, - generics.where_clause.span, - context.config.brace_style(), - Shape::legacy(where_budget, offset.block_only()), - where_on_new_line, - "{", - None, - pos_before_where, - option, - )?; - // If the where-clause cannot fit on the same line, - // put the where-clause on a new line - if !where_clause_str.contains('\n') - && last_line_width(&result) + where_clause_str.len() + offset.width() - > context.config.comment_width() - { - let width = offset.block_indent + context.config.tab_spaces() - 1; - let where_indent = Indent::new(0, width); - result.push_str(&where_indent.to_string_with_newline(context.config)); - } - result.push_str(&where_clause_str); - } else { - let item_snippet = context.snippet(item.span); - if let Some(lo) = item_snippet.find('/') { - // 1 = `{` - let comment_hi = if generics.params.len() > 0 { - generics.span.lo() - BytePos(1) - } else { - body_lo - BytePos(1) - }; - let comment_lo = item.span.lo() + BytePos(lo as u32); - if comment_lo < comment_hi { - match recover_missing_comment_in_span( - mk_sp(comment_lo, comment_hi), - Shape::indented(offset, context.config), - context, - last_line_width(&result), - ) { - Some(ref missing_comment) if !missing_comment.is_empty() => { - result.push_str(missing_comment); - } - _ => (), + let comment_lo = item.span.lo() + BytePos(lo as u32); + if comment_lo < comment_hi { + match recover_missing_comment_in_span( + mk_sp(comment_lo, comment_hi), + Shape::indented(offset, context.config), + context, + last_line_width(&result), + ) { + Some(ref missing_comment) if !missing_comment.is_empty() => { + result.push_str(missing_comment); } + _ => (), } } } + } - let block_span = mk_sp(generics.where_clause.span.hi(), item.span.hi()); - let snippet = context.snippet(block_span); - let open_pos = snippet.find_uncommented("{")? + 1; + let block_span = mk_sp(generics.where_clause.span.hi(), item.span.hi()); + let snippet = context.snippet(block_span); + let open_pos = snippet.find_uncommented("{")? + 1; - match context.config.brace_style() { - _ if last_line_contains_single_line_comment(&result) - || last_line_width(&result) + 2 > context.budget(offset.width()) => - { - result.push_str(&offset.to_string_with_newline(context.config)); - } - _ if context.config.empty_item_single_line() - && items.is_empty() - && !result.contains('\n') - && !contains_comment(&snippet[open_pos..]) => + match context.config.brace_style() { + _ if last_line_contains_single_line_comment(&result) + || last_line_width(&result) + 2 > context.budget(offset.width()) => + { + result.push_str(&offset.to_string_with_newline(context.config)); + } + _ if context.config.empty_item_single_line() + && items.is_empty() + && !result.contains('\n') + && !contains_comment(&snippet[open_pos..]) => + { + result.push_str(" {}"); + return Some(result); + } + BraceStyle::AlwaysNextLine => { + result.push_str(&offset.to_string_with_newline(context.config)); + } + BraceStyle::PreferSameLine => result.push(' '), + BraceStyle::SameLineWhere => { + if result.contains('\n') + || (!generics.where_clause.predicates.is_empty() && !items.is_empty()) { - result.push_str(" {}"); - return Some(result); - } - BraceStyle::AlwaysNextLine => { result.push_str(&offset.to_string_with_newline(context.config)); - } - BraceStyle::PreferSameLine => result.push(' '), - BraceStyle::SameLineWhere => { - if result.contains('\n') - || (!generics.where_clause.predicates.is_empty() && !items.is_empty()) - { - result.push_str(&offset.to_string_with_newline(context.config)); - } else { - result.push(' '); - } + } else { + result.push(' '); } } - result.push('{'); - - let outer_indent_str = offset.block_only().to_string_with_newline(context.config); + } + result.push('{'); - if !items.is_empty() || contains_comment(&snippet[open_pos..]) { - let mut visitor = FmtVisitor::from_context(context); - visitor.block_indent = offset.block_only().block_indent(context.config); - visitor.last_pos = block_span.lo() + BytePos(open_pos as u32); + let outer_indent_str = offset.block_only().to_string_with_newline(context.config); - for item in items { - visitor.visit_trait_item(item); - } + if !items.is_empty() || contains_comment(&snippet[open_pos..]) { + let mut visitor = FmtVisitor::from_context(context); + visitor.block_indent = offset.block_only().block_indent(context.config); + visitor.last_pos = block_span.lo() + BytePos(open_pos as u32); - visitor.format_missing(item.span.hi() - BytePos(1)); + for item in items { + visitor.visit_trait_item(item); + } - let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config); + visitor.format_missing(item.span.hi() - BytePos(1)); - result.push_str(&inner_indent_str); - result.push_str(visitor.buffer.trim()); - result.push_str(&outer_indent_str); - } else if result.contains('\n') { - result.push_str(&outer_indent_str); - } + let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config); - result.push('}'); - Some(result) - } else { - unreachable!(); + result.push_str(&inner_indent_str); + result.push_str(visitor.buffer.trim()); + result.push_str(&outer_indent_str); + } else if result.contains('\n') { + result.push_str(&outer_indent_str); } + + result.push('}'); + Some(result) } pub(crate) struct TraitAliasBounds<'a> { @@ -1322,7 +1331,7 @@ impl<'a> Rewrite for TraitAliasBounds<'a> { shape.indent.to_string_with_newline(context.config) }; - Some(format!("{}{}{}", generic_bounds_str, space, where_str)) + Some(format!("{generic_bounds_str}{space}{where_str}")) } } @@ -1339,7 +1348,7 @@ pub(crate) fn format_trait_alias( let g_shape = shape.offset_left(6)?.sub_width(2)?; let generics_str = rewrite_generics(context, alias, generics, g_shape)?; let vis_str = format_visibility(context, vis); - let lhs = format!("{}trait {} =", vis_str, generics_str); + let lhs = format!("{vis_str}trait {generics_str} ="); // 1 = ";" let trait_alias_bounds = TraitAliasBounds { generic_bounds, @@ -1376,7 +1385,7 @@ fn format_unit_struct( } else { String::new() }; - Some(format!("{}{};", header_str, generics_str)) + Some(format!("{header_str}{generics_str};")) } pub(crate) fn format_struct_struct( @@ -1466,7 +1475,7 @@ pub(crate) fn format_struct_struct( && items_str.len() <= one_line_budget && !last_line_contains_single_line_comment(&items_str) { - Some(format!("{} {} }}", result, items_str)) + Some(format!("{result} {items_str} }}")) } else { Some(format!( "{}\n{}{}\n{}}}", @@ -1696,7 +1705,7 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( rewrite_ty(rw_info, Some(bounds), ty_opt, vis) }?; match defaultness { - ast::Defaultness::Default(..) => Some(format!("default {}", result)), + ast::Defaultness::Default(..) => Some(format!("default {result}")), _ => Some(result), } } @@ -1803,14 +1812,14 @@ fn rewrite_ty<R: Rewrite>( true, )? } - _ => format!("{}=", result), + _ => format!("{result}="), }; // 1 = `;` let shape = Shape::indented(indent, context.config).sub_width(1)?; rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape).map(|s| s + ";") } else { - Some(format!("{};", result)) + Some(format!("{result};")) } } @@ -2019,7 +2028,7 @@ fn rewrite_static( let expr_lo = expr.span.lo(); let comments_span = mk_sp(comments_lo, expr_lo); - let lhs = format!("{}{} =", prefix, ty_str); + let lhs = format!("{prefix}{ty_str} ="); // 1 = ; let remaining_width = context.budget(offset.block_indent + 1); @@ -2036,7 +2045,7 @@ fn rewrite_static( .and_then(|res| recover_comment_removed(res, static_parts.span, context)) .map(|s| if s.ends_with(';') { s } else { s + ";" }) } else { - Some(format!("{}{};", prefix, ty_str)) + Some(format!("{prefix}{ty_str};")) } } @@ -2229,7 +2238,7 @@ fn rewrite_explicit_self( Some(combine_strs_with_missing_comments( context, param_attrs, - &format!("&{} {}self", lifetime_str, mut_str), + &format!("&{lifetime_str} {mut_str}self"), span, shape, !has_multiple_attr_lines, @@ -2238,7 +2247,7 @@ fn rewrite_explicit_self( None => Some(combine_strs_with_missing_comments( context, param_attrs, - &format!("&{}self", mut_str), + &format!("&{mut_str}self"), span, shape, !has_multiple_attr_lines, @@ -2909,7 +2918,7 @@ fn rewrite_where_clause_rfc_style( clause_shape.indent.to_string_with_newline(context.config) }; - Some(format!("{}{}{}", where_keyword, clause_sep, preds_str)) + Some(format!("{where_keyword}{clause_sep}{preds_str}")) } /// Rewrite `where` and comment around it. @@ -2949,8 +2958,8 @@ fn rewrite_where_keyword( let newline_before_where = comment_separator(&comment_before, shape); let newline_after_where = comment_separator(&comment_after, clause_shape); let result = format!( - "{}{}{}where{}{}", - starting_newline, comment_before, newline_before_where, newline_after_where, comment_after + "{starting_newline}{comment_before}{newline_before_where}where\ +{newline_after_where}{comment_after}" ); let allow_single_line = where_clause_option.allow_single_line && comment_before.is_empty() @@ -3001,10 +3010,12 @@ fn rewrite_bounds_on_where_clause( DefinitiveListTactic::Vertical }; + let preserve_newline = context.config.version() == Version::One; + let fmt = ListFormatting::new(shape, context.config) .tactic(shape_tactic) .trailing_separator(comma_tactic) - .preserve_newline(true); + .preserve_newline(preserve_newline); write_list(&items.collect::<Vec<_>>(), &fmt) } @@ -3105,7 +3116,7 @@ fn rewrite_where_clause( preds_str )) } else { - Some(format!(" where {}", preds_str)) + Some(format!(" where {preds_str}")) } } @@ -3234,7 +3245,7 @@ fn format_generics( if brace_pos == BracePos::None { span.hi() } else { - context.snippet_provider.span_before(span, "{") + context.snippet_provider.span_before_last(span, "{") }, ), shape, diff --git a/src/lib.rs b/src/lib.rs index 567628f63bd..8800e200fa4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,13 +3,12 @@ #![warn(unreachable_pub)] #![recursion_limit = "256"] #![allow(clippy::match_like_matches_macro)] -#![allow(unreachable_pub)] #[cfg(test)] #[macro_use] extern crate lazy_static; #[macro_use] -extern crate log; +extern crate tracing; // N.B. these crates are loaded from the sysroot, so they need extern crate. extern crate rustc_ast; @@ -29,6 +28,7 @@ extern crate thin_vec; extern crate rustc_driver; use std::cell::RefCell; +use std::cmp::min; use std::collections::HashMap; use std::fmt; use std::io::{self, Write}; @@ -386,9 +386,15 @@ fn format_code_block( .snippet .rfind('}') .unwrap_or_else(|| formatted.snippet.len()); + + // It's possible that `block_len < FN_MAIN_PREFIX.len()`. This can happen if the code block was + // formatted into the empty string, leading to the enclosing `fn main() {\n}` being formatted + // into `fn main() {}`. In this case no unindentation is done. + let block_start = min(FN_MAIN_PREFIX.len(), block_len); + let mut is_indented = true; let indent_str = Indent::from_width(config, config.tab_spaces()).to_string(config); - for (kind, ref line) in LineClasses::new(&formatted.snippet[FN_MAIN_PREFIX.len()..block_len]) { + for (kind, ref line) in LineClasses::new(&formatted.snippet[block_start..block_len]) { if !is_first { result.push('\n'); } else { diff --git a/src/lists.rs b/src/lists.rs index a878e6cf9b2..41afef279e9 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -637,7 +637,7 @@ pub(crate) fn extract_post_comment( post_snippet.trim_matches(white_space) } // not comment or over two lines - else if post_snippet.ends_with(',') + else if post_snippet.ends_with(separator) && (!post_snippet.trim().starts_with("//") || post_snippet.trim().contains('\n')) { post_snippet[..(post_snippet.len() - 1)].trim_matches(white_space) diff --git a/src/macros.rs b/src/macros.rs index 4f45d0c7402..76553466e48 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -103,7 +103,7 @@ fn rewrite_macro_name( format!("{}!", pprust::path_to_string(path)) }; match extra_ident { - Some(ident) if ident.name != kw::Empty => format!("{} {}", name, ident), + Some(ident) if ident.name != kw::Empty => format!("{name} {ident}"), _ => name, } } @@ -214,14 +214,14 @@ fn rewrite_macro_inner( if ts.is_empty() && !has_comment { return match style { Delimiter::Parenthesis if position == MacroPosition::Item => { - Some(format!("{}();", macro_name)) + Some(format!("{macro_name}();")) } Delimiter::Bracket if position == MacroPosition::Item => { - Some(format!("{}[];", macro_name)) + Some(format!("{macro_name}[];")) } - Delimiter::Parenthesis => Some(format!("{}()", macro_name)), - Delimiter::Bracket => Some(format!("{}[]", macro_name)), - Delimiter::Brace => Some(format!("{} {{}}", macro_name)), + Delimiter::Parenthesis => Some(format!("{macro_name}()")), + Delimiter::Bracket => Some(format!("{macro_name}[]")), + Delimiter::Brace => Some(format!("{macro_name} {{}}")), _ => unreachable!(), }; } @@ -255,6 +255,7 @@ fn rewrite_macro_inner( ¯o_name, shape, style, + original_style, position, mac.span(), ); @@ -295,20 +296,19 @@ fn rewrite_macro_inner( // If we are rewriting `vec!` macro or other special macros, // then we can rewrite this as a usual array literal. // Otherwise, we must preserve the original existence of trailing comma. - let macro_name = ¯o_name.as_str(); let mut force_trailing_comma = if trailing_comma { Some(SeparatorTactic::Always) } else { Some(SeparatorTactic::Never) }; - if FORCED_BRACKET_MACROS.contains(macro_name) && !is_nested_macro { + if is_forced_bracket && !is_nested_macro { context.leave_macro(); if context.use_block_indent() { force_trailing_comma = Some(SeparatorTactic::Vertical); }; } let rewrite = rewrite_array( - macro_name, + ¯o_name, arg_vec.iter(), mac.span(), context, @@ -321,7 +321,7 @@ fn rewrite_macro_inner( _ => "", }; - Some(format!("{}{}", rewrite, comma)) + Some(format!("{rewrite}{comma}")) } } Delimiter::Brace => { @@ -330,8 +330,8 @@ fn rewrite_macro_inner( // anything in between the braces (for now). let snippet = context.snippet(mac.span()).trim_start_matches(|c| c != '{'); match trim_left_preserve_layout(snippet, shape.indent, context.config) { - Some(macro_body) => Some(format!("{} {}", macro_name, macro_body)), - None => Some(format!("{} {}", macro_name, snippet)), + Some(macro_body) => Some(format!("{macro_name} {macro_body}")), + None => Some(format!("{macro_name} {snippet}")), } } _ => unreachable!(), @@ -362,7 +362,7 @@ fn handle_vec_semi( && lhs.len() + rhs.len() + total_overhead <= shape.width { // macro_name(lhs; rhs) or macro_name[lhs; rhs] - Some(format!("{}{}{}; {}{}", macro_name, left, lhs, rhs, right)) + Some(format!("{macro_name}{left}{lhs}; {rhs}{right}")) } else { // macro_name(\nlhs;\nrhs\n) or macro_name[\nlhs;\nrhs\n] Some(format!( @@ -379,6 +379,23 @@ fn handle_vec_semi( } } +fn rewrite_empty_macro_def_body( + context: &RewriteContext<'_>, + span: Span, + shape: Shape, +) -> Option<String> { + // Create an empty, dummy `ast::Block` representing an empty macro body + let block = ast::Block { + stmts: vec![].into(), + id: rustc_ast::node_id::DUMMY_NODE_ID, + rules: ast::BlockCheckMode::Default, + span: span, + tokens: None, + could_be_bare_literal: false, + }; + block.rewrite(context, shape) +} + pub(crate) fn rewrite_macro_def( context: &RewriteContext<'_>, shape: Shape, @@ -419,6 +436,13 @@ pub(crate) fn rewrite_macro_def( shape }; + if parsed_def.branches.len() == 0 { + let lo = context.snippet_provider.span_before(span, "{"); + result += " "; + result += &rewrite_empty_macro_def_body(context, span.with_lo(lo), shape)?; + return Some(result); + } + let branch_items = itemize_list( context.snippet_provider, parsed_def.branches.iter(), @@ -572,8 +596,8 @@ fn delim_token_to_str( .block_indent(context.config) .to_string_with_newline(context.config); ( - format!("{}{}", lhs, nested_indent_str), - format!("{}{}", indent_str, rhs), + format!("{lhs}{nested_indent_str}"), + format!("{indent_str}{rhs}"), ) } else { (lhs.to_owned(), rhs.to_owned()) @@ -630,7 +654,7 @@ impl MacroArgKind { }; match *self { - MacroArgKind::MetaVariable(ty, ref name) => Some(format!("${}:{}", name, ty)), + MacroArgKind::MetaVariable(ty, ref name) => Some(format!("${name}:{ty}")), MacroArgKind::Repeat(delim_tok, ref args, ref another, ref tok) => { let (lhs, inner, rhs) = rewrite_delimited_inner(delim_tok, args)?; let another = another @@ -639,14 +663,14 @@ impl MacroArgKind { .unwrap_or_else(|| "".to_owned()); let repeat_tok = pprust::token_to_string(tok); - Some(format!("${}{}{}{}{}", lhs, inner, rhs, another, repeat_tok)) + Some(format!("${lhs}{inner}{rhs}{another}{repeat_tok}")) } MacroArgKind::Delimited(delim_tok, ref args) => { rewrite_delimited_inner(delim_tok, args) .map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs)) } - MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{}{} ", prefix, sep)), - MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{}{}", prefix, inner)), + MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{prefix}{sep} ")), + MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{prefix}{inner}")), } } } @@ -1378,15 +1402,19 @@ fn rewrite_macro_with_items( macro_name: &str, shape: Shape, style: Delimiter, + original_style: Delimiter, position: MacroPosition, span: Span, ) -> Option<String> { - let (opener, closer) = match style { - Delimiter::Parenthesis => ("(", ")"), - Delimiter::Bracket => ("[", "]"), - Delimiter::Brace => (" {", "}"), - _ => return None, + let style_to_delims = |style| match style { + Delimiter::Parenthesis => Some(("(", ")")), + Delimiter::Bracket => Some(("[", "]")), + Delimiter::Brace => Some((" {", "}")), + _ => None, }; + + let (opener, closer) = style_to_delims(style)?; + let (original_opener, _) = style_to_delims(original_style)?; let trailing_semicolon = match style { Delimiter::Parenthesis | Delimiter::Bracket if position == MacroPosition::Item => ";", _ => "", @@ -1394,7 +1422,13 @@ fn rewrite_macro_with_items( let mut visitor = FmtVisitor::from_context(context); visitor.block_indent = shape.indent.block_indent(context.config); - visitor.last_pos = context.snippet_provider.span_after(span, opener.trim()); + + // The current opener may be different from the original opener. This can happen + // if our macro is a forced bracket macro originally written with non-bracket + // delimiters. We need to use the original opener to locate the span after it. + visitor.last_pos = context + .snippet_provider + .span_after(span, original_opener.trim()); for item in items { let item = match item { MacroArg::Item(item) => item, diff --git a/src/matches.rs b/src/matches.rs index 4c37116f120..95b0ed16db8 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -124,7 +124,7 @@ pub(crate) fn rewrite_match( if arms.is_empty() { let snippet = context.snippet(mk_sp(open_brace_pos, span.hi() - BytePos(1))); if snippet.trim().is_empty() { - Some(format!("match {} {{}}", cond_str)) + Some(format!("match {cond_str} {{}}")) } else { // Empty match with comments or inner attributes? We are not going to bother, sorry ;) Some(context.snippet(span).to_owned()) @@ -246,8 +246,18 @@ fn rewrite_match_arm( }; // Patterns - // 5 = ` => {` - let pat_shape = shape.sub_width(5)?.offset_left(pipe_offset)?; + let pat_shape = match &arm.body.kind { + ast::ExprKind::Block(_, Some(label)) => { + // Some block with a label ` => 'label: {` + // 7 = ` => : {` + let label_len = label.ident.as_str().len(); + shape.sub_width(7 + label_len)?.offset_left(pipe_offset)? + } + _ => { + // 5 = ` => {` + shape.sub_width(5)?.offset_left(pipe_offset)? + } + }; let pats_str = arm.pat.rewrite(context, pat_shape)?; // Guard @@ -264,7 +274,7 @@ fn rewrite_match_arm( let lhs_str = combine_strs_with_missing_comments( context, &attrs_str, - &format!("{}{}{}", pipe_str, pats_str, guard_str), + &format!("{pipe_str}{pats_str}{guard_str}"), missing_span, shape, false, @@ -296,8 +306,9 @@ fn block_can_be_flattened<'a>( expr: &'a ast::Expr, ) -> Option<&'a ast::Block> { match expr.kind { - ast::ExprKind::Block(ref block, _) - if !is_unsafe_block(block) + ast::ExprKind::Block(ref block, label) + if label.is_none() + && !is_unsafe_block(block) && !context.inside_macro() && is_simple_block(context, block, Some(&expr.attrs)) && !stmt_is_expr_mac(&block.stmts[0]) => @@ -532,7 +543,7 @@ fn rewrite_guard( if let Some(cond_shape) = cond_shape { if let Some(cond_str) = guard.rewrite(context, cond_shape) { if !cond_str.contains('\n') || pattern_width <= context.config.tab_spaces() { - return Some(format!(" if {}", cond_str)); + return Some(format!(" if {cond_str}")); } } } diff --git a/src/pairs.rs b/src/pairs.rs index d135da7e359..9dac20d3699 100644 --- a/src/pairs.rs +++ b/src/pairs.rs @@ -42,9 +42,13 @@ pub(crate) fn rewrite_all_pairs( context: &RewriteContext<'_>, ) -> Option<String> { expr.flatten(context, shape).and_then(|list| { - // First we try formatting on one line. - rewrite_pairs_one_line(&list, shape, context) - .or_else(|| rewrite_pairs_multiline(&list, shape, context)) + if list.let_chain_count() > 0 && !list.can_rewrite_let_chain_single_line() { + rewrite_pairs_multiline(&list, shape, context) + } else { + // First we try formatting on one line. + rewrite_pairs_one_line(&list, shape, context) + .or_else(|| rewrite_pairs_multiline(&list, shape, context)) + } }) } @@ -234,8 +238,8 @@ where let rhs_result = rhs.rewrite(context, rhs_shape)?; let indent_str = rhs_shape.indent.to_string_with_newline(context.config); let infix_with_sep = match separator_place { - SeparatorPlace::Back => format!("{}{}", infix, indent_str), - SeparatorPlace::Front => format!("{}{}", indent_str, infix), + SeparatorPlace::Back => format!("{infix}{indent_str}"), + SeparatorPlace::Front => format!("{indent_str}{infix}"), }; Some(format!( "{}{}{}{}", @@ -255,6 +259,37 @@ struct PairList<'a, 'b, T: Rewrite> { separators: Vec<&'a str>, } +fn is_ident(expr: &ast::Expr) -> bool { + match &expr.kind { + ast::ExprKind::Path(None, path) if path.segments.len() == 1 => true, + ast::ExprKind::Unary(_, expr) + | ast::ExprKind::AddrOf(_, _, expr) + | ast::ExprKind::Paren(expr) + | ast::ExprKind::Try(expr) => is_ident(expr), + _ => false, + } +} + +impl<'a, 'b> PairList<'a, 'b, ast::Expr> { + fn let_chain_count(&self) -> usize { + self.list + .iter() + .filter(|(expr, _)| matches!(expr.kind, ast::ExprKind::Let(_, _, _))) + .count() + } + + fn can_rewrite_let_chain_single_line(&self) -> bool { + if self.list.len() != 2 { + return false; + } + + let fist_item_is_ident = is_ident(self.list[0].0); + let second_item_is_let_chain = matches!(self.list[1].0.kind, ast::ExprKind::Let(_, _, _)); + + fist_item_is_ident && second_item_is_let_chain + } +} + impl FlattenPair for ast::Expr { fn flatten( &self, diff --git a/src/parse/session.rs b/src/parse/session.rs index 3f94bb29933..0573df9de2f 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -316,8 +316,7 @@ impl LineRangeUtils for ParseSess { debug_assert_eq!( lo.sf.name, hi.sf.name, - "span crossed file boundary: lo: {:?}, hi: {:?}", - lo, hi + "span crossed file boundary: lo: {lo:?}, hi: {hi:?}" ); // in case the span starts with a newline, the line range is off by 1 without the diff --git a/src/patterns.rs b/src/patterns.rs index 3f335172590..33f3b4b8a21 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -208,7 +208,7 @@ impl Rewrite for Pat { None => "", Some(_) => " ", }; - format!("{}{}{}", lhs_spacing, infix, rhs_spacing) + format!("{lhs_spacing}{infix}{rhs_spacing}") } else { infix.to_owned() }; @@ -283,7 +283,7 @@ fn rewrite_struct_pat( let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?; if fields.is_empty() && !ellipsis { - return Some(format!("{} {{}}", path_str)); + return Some(format!("{path_str} {{}}")); } let (ellipsis_str, terminator) = if ellipsis { (", ..", "..") } else { ("", "}") }; @@ -344,7 +344,7 @@ fn rewrite_struct_pat( // ast::Pat doesn't have attrs so use &[] let fields_str = wrap_struct_field(context, &[], &fields_str, shape, v_shape, one_line_width)?; - Some(format!("{} {{{}}}", path_str, fields_str)) + Some(format!("{path_str} {{{fields_str}}}")) } impl Rewrite for PatField { @@ -376,7 +376,7 @@ impl Rewrite for PatField { let id_str = rewrite_ident(context, self.ident); let one_line_width = id_str.len() + 2 + pat_str.len(); let pat_and_id_str = if one_line_width <= shape.width { - format!("{}: {}", id_str, pat_str) + format!("{id_str}: {pat_str}") } else { format!( "{}:\n{}{}", diff --git a/src/rustfmt_diff.rs b/src/rustfmt_diff.rs index 1724a0f87bf..c9883452185 100644 --- a/src/rustfmt_diff.rs +++ b/src/rustfmt_diff.rs @@ -95,7 +95,7 @@ impl fmt::Display for ModifiedLines { )?; for line in &chunk.lines { - writeln!(f, "{}", line)?; + writeln!(f, "{line}")?; } } @@ -166,12 +166,12 @@ impl OutputWriter { if let Some(color) = color { t.fg(color).unwrap(); } - writeln!(t, "{}", msg).unwrap(); + writeln!(t, "{msg}").unwrap(); if color.is_some() { t.reset().unwrap(); } } - None => println!("{}", msg), + None => println!("{msg}"), } } } @@ -265,16 +265,15 @@ where for line in mismatch.lines { match line { DiffLine::Context(ref str) => { - writer.writeln(&format!(" {}{}", str, line_terminator), None) + writer.writeln(&format!(" {str}{line_terminator}"), None) } DiffLine::Expected(ref str) => writer.writeln( - &format!("+{}{}", str, line_terminator), + &format!("+{str}{line_terminator}"), Some(term::color::GREEN), ), - DiffLine::Resulting(ref str) => writer.writeln( - &format!("-{}{}", str, line_terminator), - Some(term::color::RED), - ), + DiffLine::Resulting(ref str) => { + writer.writeln(&format!("-{str}{line_terminator}"), Some(term::color::RED)) + } } } } diff --git a/src/skip.rs b/src/skip.rs index 68f85b2ade4..d733f7068fd 100644 --- a/src/skip.rs +++ b/src/skip.rs @@ -105,7 +105,7 @@ pub(crate) fn is_skip_attr(segments: &[ast::PathSegment]) -> bool { fn get_skip_names(kind: &str, attrs: &[ast::Attribute]) -> Vec<String> { let mut skip_names = vec![]; - let path = format!("{}::{}::{}", RUSTFMT, SKIP, kind); + let path = format!("{RUSTFMT}::{SKIP}::{kind}"); for attr in attrs { // rustc_ast::ast::Path is implemented partialEq // but it is designed for segments.len() == 1 diff --git a/src/source_file.rs b/src/source_file.rs index 56d4ab40038..958f9b0154f 100644 --- a/src/source_file.rs +++ b/src/source_file.rs @@ -62,7 +62,7 @@ where fn ensure_real_path(filename: &FileName) -> &Path { match *filename { FileName::Real(ref path) => path, - _ => panic!("cannot format `{}` and emit to files", filename), + _ => panic!("cannot format `{filename}` and emit to files"), } } diff --git a/src/stmt.rs b/src/stmt.rs index 0b3854425ea..e3fe4ebca11 100644 --- a/src/stmt.rs +++ b/src/stmt.rs @@ -3,7 +3,7 @@ use rustc_span::Span; use crate::comment::recover_comment_removed; use crate::config::Version; -use crate::expr::{format_expr, ExprType}; +use crate::expr::{format_expr, is_simple_block, ExprType}; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::Shape; use crate::source_map::LineRangeUtils; @@ -33,6 +33,21 @@ impl<'a> Stmt<'a> { } } + pub(crate) fn from_simple_block( + context: &RewriteContext<'_>, + block: &'a ast::Block, + attrs: Option<&[ast::Attribute]>, + ) -> Option<Self> { + if is_simple_block(context, block, attrs) { + let inner = &block.stmts[0]; + // Simple blocks only contain one expr and no stmts + let is_last = true; + Some(Stmt { inner, is_last }) + } else { + None + } + } + pub(crate) fn from_ast_node(inner: &'a ast::Stmt, is_last: bool) -> Self { Stmt { inner, is_last } } @@ -80,13 +95,13 @@ impl<'a> Rewrite for Stmt<'a> { } else { ExprType::Statement }; - format_stmt(context, shape, self.as_ast_node(), expr_type) - } -} - -impl Rewrite for ast::Stmt { - fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { - format_stmt(context, shape, self, ExprType::Statement) + format_stmt( + context, + shape, + self.as_ast_node(), + expr_type, + self.is_last_expr(), + ) } } @@ -95,13 +110,14 @@ fn format_stmt( shape: Shape, stmt: &ast::Stmt, expr_type: ExprType, + is_last_expr: bool, ) -> Option<String> { skip_out_of_file_lines_range!(context, stmt.span()); let result = match stmt.kind { ast::StmtKind::Local(ref local) => local.rewrite(context, shape), ast::StmtKind::Expr(ref ex) | ast::StmtKind::Semi(ref ex) => { - let suffix = if semicolon_for_stmt(context, stmt) { + let suffix = if semicolon_for_stmt(context, stmt, is_last_expr) { ";" } else { "" diff --git a/src/string.rs b/src/string.rs index 78b72a50cb2..cb666fff695 100644 --- a/src/string.rs +++ b/src/string.rs @@ -1,7 +1,7 @@ // Format string literals. use regex::Regex; -use unicode_categories::UnicodeCategories; +use unicode_properties::{GeneralCategory, UnicodeGeneralCategory}; use unicode_segmentation::UnicodeSegmentation; use crate::config::Config; @@ -366,7 +366,7 @@ fn is_whitespace(grapheme: &str) -> bool { fn is_punctuation(grapheme: &str) -> bool { grapheme .chars() - .all(UnicodeCategories::is_punctuation_other) + .all(|c| c.general_category() == GeneralCategory::OtherPunctuation) } fn graphemes_width(graphemes: &[&str]) -> usize { diff --git a/src/test/configuration_snippet.rs b/src/test/configuration_snippet.rs index c70b3c5facd..80b61c88a00 100644 --- a/src/test/configuration_snippet.rs +++ b/src/test/configuration_snippet.rs @@ -233,13 +233,11 @@ impl ConfigCodeBlock { Some(ConfigurationSection::ConfigName(name)) => { assert!( Config::is_valid_name(&name), - "an unknown configuration option was found: {}", - name + "an unknown configuration option was found: {name}" ); assert!( hash_set.remove(&name), - "multiple configuration guides found for option {}", - name + "multiple configuration guides found for option {name}" ); code_block.set_config_name(Some(name)); } @@ -266,7 +264,7 @@ fn configuration_snippet_tests() { // Display results. println!("Ran {} configurations tests.", blocks.len()); - assert_eq!(failures, 0, "{} configurations tests failed", failures); + assert_eq!(failures, 0, "{failures} configurations tests failed"); } // Read Configurations.md and build a `Vec` of `ConfigCodeBlock` structs with one @@ -289,7 +287,7 @@ fn get_code_blocks() -> Vec<ConfigCodeBlock> { for name in hash_set { if !Config::is_hidden_option(&name) { - panic!("{} does not have a configuration guide", name); + panic!("{name} does not have a configuration guide"); } } diff --git a/src/test/mod.rs b/src/test/mod.rs index 37854ead28b..47f89c1871a 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -47,7 +47,7 @@ const FILE_SKIP_LIST: &[&str] = &[ ]; fn init_log() { - let _ = env_logger::builder().is_test(true).try_init(); + let _ = tracing_subscriber::fmt().with_test_writer().try_init(); } struct TestSetting { @@ -203,8 +203,8 @@ fn coverage_tests() { let files = get_test_files(Path::new("tests/coverage/source"), true); let (_reports, count, fails) = check_files(files, &None); - println!("Ran {} tests in coverage mode.", count); - assert_eq!(fails, 0, "{} tests failed", fails); + println!("Ran {count} tests in coverage mode."); + assert_eq!(fails, 0, "{fails} tests failed"); } #[test] @@ -396,8 +396,8 @@ fn self_tests() { let mut warnings = 0; // Display results. - println!("Ran {} self tests.", count); - assert_eq!(fails, 0, "{} self tests failed", fails); + println!("Ran {count} self tests."); + assert_eq!(fails, 0, "{fails} self tests failed"); for format_report in reports { println!( @@ -407,11 +407,7 @@ fn self_tests() { warnings += format_report.warning_count(); } - assert_eq!( - warnings, 0, - "Rustfmt's code generated {} warnings", - warnings - ); + assert_eq!(warnings, 0, "Rustfmt's code generated {warnings} warnings"); } #[test] @@ -606,7 +602,7 @@ fn stdin_handles_mod_inner_ignore_attr() { fn format_lines_errors_are_reported() { init_log(); let long_identifier = String::from_utf8(vec![b'a'; 239]).unwrap(); - let input = Input::Text(format!("fn {}() {{}}", long_identifier)); + let input = Input::Text(format!("fn {long_identifier}() {{}}")); let mut config = Config::default(); config.set().error_on_line_overflow(true); let mut session = Session::<io::Stdout>::new(config, None); @@ -618,7 +614,7 @@ fn format_lines_errors_are_reported() { fn format_lines_errors_are_reported_with_tabs() { init_log(); let long_identifier = String::from_utf8(vec![b'a'; 97]).unwrap(); - let input = Input::Text(format!("fn a() {{\n\t{}\n}}", long_identifier)); + let input = Input::Text(format!("fn a() {{\n\t{long_identifier}\n}}")); let mut config = Config::default(); config.set().error_on_line_overflow(true); config.set().hard_tabs(true); @@ -829,11 +825,11 @@ fn handle_result( for (file_name, fmt_text) in result { // If file is in tests/source, compare to file with same name in tests/target. let target = get_target(&file_name, target); - let open_error = format!("couldn't open target {:?}", target); + let open_error = format!("couldn't open target {target:?}"); let mut f = fs::File::open(&target).expect(&open_error); let mut text = String::new(); - let read_error = format!("failed reading target {:?}", target); + let read_error = format!("failed reading target {target:?}"); f.read_to_string(&mut text).expect(&read_error); // Ignore LF and CRLF difference for Windows. diff --git a/src/types.rs b/src/types.rs index 5e8edd8f8bf..127aff913e3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -301,7 +301,7 @@ where let output = match *output { FnRetTy::Ty(ref ty) => { let type_str = ty.rewrite(context, ty_shape)?; - format!(" -> {}", type_str) + format!(" -> {type_str}") } FnRetTy::Default(..) => String::new(), }; @@ -373,7 +373,7 @@ where || !context.use_block_indent() || is_inputs_empty { - format!("({})", list_str) + format!("({list_str})") } else { format!( "({}{}{})", @@ -383,7 +383,7 @@ where ) }; if output.is_empty() || last_line_width(&args) + first_line_width(&output) <= shape.width { - Some(format!("{}{}", args, output)) + Some(format!("{args}{output}")) } else { Some(format!( "{}\n{}{}", @@ -426,12 +426,12 @@ impl Rewrite for ast::WherePredicate { }) => { let type_str = bounded_ty.rewrite(context, shape)?; let colon = type_bound_colon(context).trim_end(); - let lhs = if let Some(lifetime_str) = - rewrite_lifetime_param(context, shape, bound_generic_params) + let lhs = if let Some(binder_str) = + rewrite_bound_params(context, shape, bound_generic_params) { - format!("for<{}> {}{}", lifetime_str, type_str, colon) + format!("for<{binder_str}> {type_str}{colon}") } else { - format!("{}{}", type_str, colon) + format!("{type_str}{colon}") }; rewrite_assign_rhs(context, lhs, bounds, &RhsAssignKind::Bounds, shape)? @@ -657,8 +657,7 @@ impl Rewrite for ast::GenericParam { impl Rewrite for ast::PolyTraitRef { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { - if let Some(lifetime_str) = - rewrite_lifetime_param(context, shape, &self.bound_generic_params) + if let Some(lifetime_str) = rewrite_bound_params(context, shape, &self.bound_generic_params) { // 6 is "for<> ".len() let extra_offset = lifetime_str.len() + 6; @@ -666,7 +665,7 @@ impl Rewrite for ast::PolyTraitRef { .trait_ref .rewrite(context, shape.offset_left(extra_offset)?)?; - Some(format!("for<{}> {}", lifetime_str, path_str)) + Some(format!("for<{lifetime_str}> {path_str}")) } else { self.trait_ref.rewrite(context, shape) } @@ -684,9 +683,11 @@ impl Rewrite for ast::Ty { match self.kind { ast::TyKind::TraitObject(ref bounds, tobj_syntax) => { // we have to consider 'dyn' keyword is used or not!!! - let is_dyn = tobj_syntax == ast::TraitObjectSyntax::Dyn; - // 4 is length of 'dyn ' - let shape = if is_dyn { shape.offset_left(4)? } else { shape }; + let (shape, prefix) = match tobj_syntax { + ast::TraitObjectSyntax::Dyn => (shape.offset_left(4)?, "dyn "), + ast::TraitObjectSyntax::DynStar => (shape.offset_left(5)?, "dyn* "), + ast::TraitObjectSyntax::None => (shape, ""), + }; let mut res = bounds.rewrite(context, shape)?; // We may have falsely removed a trailing `+` inside macro call. if context.inside_macro() && bounds.len() == 1 { @@ -694,11 +695,7 @@ impl Rewrite for ast::Ty { res.push('+'); } } - if is_dyn { - Some(format!("dyn {}", res)) - } else { - Some(res) - } + Some(format!("{prefix}{res}")) } ast::TyKind::Ptr(ref mt) => { let prefix = match mt.mutbl { @@ -794,7 +791,7 @@ impl Rewrite for ast::Ty { if let Some(sh) = shape.sub_width(2) { if let Some(ref s) = ty.rewrite(context, sh) { if !s.contains('\n') { - return Some(format!("({})", s)); + return Some(format!("({s})")); } } } @@ -883,8 +880,7 @@ fn rewrite_bare_fn( let mut result = String::with_capacity(128); - if let Some(ref lifetime_str) = rewrite_lifetime_param(context, shape, &bare_fn.generic_params) - { + if let Some(ref lifetime_str) = rewrite_bound_params(context, shape, &bare_fn.generic_params) { result.push_str("for<"); // 6 = "for<> ".len(), 4 = "for<". // This doesn't work out so nicely for multiline situation with lots of @@ -898,7 +894,6 @@ fn rewrite_bare_fn( result.push_str(&format_extern( bare_fn.ext, context.config.force_explicit_abi(), - false, )); result.push_str("fn"); @@ -1124,16 +1119,15 @@ pub(crate) fn can_be_overflowed_type( } } -/// Returns `None` if there is no `LifetimeDef` in the given generic parameters. -pub(crate) fn rewrite_lifetime_param( +/// Returns `None` if there is no `GenericParam` in the list +pub(crate) fn rewrite_bound_params( context: &RewriteContext<'_>, shape: Shape, generic_params: &[ast::GenericParam], ) -> Option<String> { let result = generic_params .iter() - .filter(|p| matches!(p.kind, ast::GenericParamKind::Lifetime)) - .map(|lt| lt.rewrite(context, shape)) + .map(|param| param.rewrite(context, shape)) .collect::<Option<Vec<_>>>()? .join(", "); if result.is_empty() { diff --git a/src/utils.rs b/src/utils.rs index 4fc5a9b6896..79a759d68ce 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -69,7 +69,7 @@ pub(crate) fn format_visibility( let path = segments_iter.collect::<Vec<_>>().join("::"); let in_str = if is_keyword(&path) { "" } else { "in " }; - Cow::from(format!("pub({}{}) ", in_str, path)) + Cow::from(format!("pub({in_str}{path}) ")) } } } @@ -131,23 +131,18 @@ pub(crate) fn format_mutability(mutability: ast::Mutability) -> &'static str { } #[inline] -pub(crate) fn format_extern( - ext: ast::Extern, - explicit_abi: bool, - is_mod: bool, -) -> Cow<'static, str> { - let abi = match ext { - ast::Extern::None => "Rust".to_owned(), - ast::Extern::Implicit(_) => "C".to_owned(), - ast::Extern::Explicit(abi, _) => abi.symbol_unescaped.to_string(), - }; - - if abi == "Rust" && !is_mod { - Cow::from("") - } else if abi == "C" && !explicit_abi { - Cow::from("extern ") - } else { - Cow::from(format!(r#"extern "{}" "#, abi)) +pub(crate) fn format_extern(ext: ast::Extern, explicit_abi: bool) -> Cow<'static, str> { + match ext { + ast::Extern::None => Cow::from(""), + ast::Extern::Implicit(_) if explicit_abi => Cow::from("extern \"C\" "), + ast::Extern::Implicit(_) => Cow::from("extern "), + // turn `extern "C"` into `extern` when `explicit_abi` is set to false + ast::Extern::Explicit(abi, _) if abi.symbol_unescaped == sym::C && !explicit_abi => { + Cow::from("extern ") + } + ast::Extern::Explicit(abi, _) => { + Cow::from(format!(r#"extern "{}" "#, abi.symbol_unescaped)) + } } } @@ -292,14 +287,20 @@ pub(crate) fn semicolon_for_expr(context: &RewriteContext<'_>, expr: &ast::Expr) } #[inline] -pub(crate) fn semicolon_for_stmt(context: &RewriteContext<'_>, stmt: &ast::Stmt) -> bool { +pub(crate) fn semicolon_for_stmt( + context: &RewriteContext<'_>, + stmt: &ast::Stmt, + is_last_expr: bool, +) -> bool { match stmt.kind { ast::StmtKind::Semi(ref expr) => match expr.kind { ast::ExprKind::While(..) | ast::ExprKind::Loop(..) | ast::ExprKind::ForLoop(..) => { false } ast::ExprKind::Break(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Ret(..) => { - context.config.trailing_semicolon() + // The only time we can skip the semi-colon is if the config option is set to false + // **and** this is the last expr (even though any following exprs are unreachable) + context.config.trailing_semicolon() || !is_last_expr } _ => true, }, |
