From 6db6bafc61b6f4f782c5f10e8dbe3f006c41fc5d Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 29 Dec 2021 20:49:39 -0600 Subject: [PATCH 01/64] Merge commit '4a053f206fd6799a25823c307f7d7f9d897be118' into sync-rustfmt-subtree --- rust-toolchain | 2 +- src/bin/main.rs | 2 +- src/closures.rs | 14 +- src/config/file_lines.rs | 8 +- src/config/options.rs | 14 +- src/expr.rs | 23 +- src/formatting.rs | 56 +++- src/items.rs | 10 +- src/lib.rs | 5 +- src/lists.rs | 4 +- src/macros.rs | 258 ++---------------- src/matches.rs | 6 +- src/modules.rs | 4 +- src/modules/visitor.rs | 6 +- src/parse/macros/asm.rs | 11 + src/parse/macros/cfg_if.rs | 89 ++++++ src/parse/macros/lazy_static.rs | 50 ++++ src/parse/macros/mod.rs | 231 ++++++++++++++++ src/parse/mod.rs | 3 + src/{syntux => parse}/parser.rs | 91 +----- src/{syntux => parse}/session.rs | 2 +- src/patterns.rs | 7 +- src/rewrite.rs | 2 +- src/rustfmt_diff.rs | 10 +- src/source_file.rs | 2 +- src/test/configuration_snippet.rs | 21 +- src/test/mod_resolver.rs | 9 + src/visitor.rs | 27 +- .../mod-resolver/skip-files-issue-5065/foo.rs | 5 + .../skip-files-issue-5065/foo/bar/baz.rs | 1 + .../skip-files-issue-5065/main.rs | 9 + .../mod-resolver/skip-files-issue-5065/one.rs | 1 + tests/source/async_block.rs | 16 ++ tests/source/match.rs | 19 ++ tests/target/async_block.rs | 10 + ...railing_comma_always_struct_lit_width_0.rs | 10 + ...trailing_comma_never_struct_lit_width_0.rs | 10 + ..._line_struct_with_trailing_comma_always.rs | 10 + ...i_line_struct_with_trailing_comma_never.rs | 10 + .../issue-5066/with_trailing_comma_always.rs | 5 + .../issue-5066/with_trailing_comma_never.rs | 5 + tests/target/issue-5151/minimum_example.rs | 16 ++ tests/target/match.rs | 19 ++ .../target/skip/preserve_trailing_comment.rs | 7 + 44 files changed, 710 insertions(+), 410 deletions(-) create mode 100644 src/parse/macros/asm.rs create mode 100644 src/parse/macros/cfg_if.rs create mode 100644 src/parse/macros/lazy_static.rs create mode 100644 src/parse/macros/mod.rs create mode 100644 src/parse/mod.rs rename src/{syntux => parse}/parser.rs (61%) rename src/{syntux => parse}/session.rs (99%) create mode 100644 tests/mod-resolver/skip-files-issue-5065/foo.rs create mode 100644 tests/mod-resolver/skip-files-issue-5065/foo/bar/baz.rs create mode 100644 tests/mod-resolver/skip-files-issue-5065/main.rs create mode 100644 tests/mod-resolver/skip-files-issue-5065/one.rs create mode 100644 tests/target/issue-5066/multi_line_struct_trailing_comma_always_struct_lit_width_0.rs create mode 100644 tests/target/issue-5066/multi_line_struct_trailing_comma_never_struct_lit_width_0.rs create mode 100644 tests/target/issue-5066/multi_line_struct_with_trailing_comma_always.rs create mode 100644 tests/target/issue-5066/multi_line_struct_with_trailing_comma_never.rs create mode 100644 tests/target/issue-5066/with_trailing_comma_always.rs create mode 100644 tests/target/issue-5066/with_trailing_comma_never.rs create mode 100644 tests/target/issue-5151/minimum_example.rs create mode 100644 tests/target/skip/preserve_trailing_comment.rs diff --git a/rust-toolchain b/rust-toolchain index 1d2cad6675117..d4cdcec2018aa 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-11-08" +channel = "nightly-2021-12-29" components = ["rustc-dev"] diff --git a/src/bin/main.rs b/src/bin/main.rs index 9d2e97c9479fc..4d845547cdfed 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -26,7 +26,7 @@ fn main() { let exit_code = match execute(&opts) { Ok(code) => code, Err(e) => { - eprintln!("{}", e.to_string()); + eprintln!("{}", e); 1 } }; diff --git a/src/closures.rs b/src/closures.rs index 34d73a77fd3d4..e688db1c39d7c 100644 --- a/src/closures.rs +++ b/src/closures.rs @@ -236,21 +236,21 @@ fn rewrite_closure_fn_decl( context: &RewriteContext<'_>, shape: Shape, ) -> Option<(String, usize)> { - let is_async = if asyncness.is_async() { "async " } else { "" }; - let mover = if capture == ast::CaptureBy::Value { - "move " + let immovable = if movability == ast::Movability::Static { + "static " } else { "" }; - let immovable = if movability == ast::Movability::Static { - "static " + let is_async = if asyncness.is_async() { "async " } else { "" }; + let mover = if capture == ast::CaptureBy::Value { + "move " } else { "" }; // 4 = "|| {".len(), which is overconservative when the closure consists of // a single expression. let nested_shape = shape - .shrink_left(is_async.len() + mover.len() + immovable.len())? + .shrink_left(immovable.len() + is_async.len() + mover.len())? .sub_width(4)?; // 1 = | @@ -288,7 +288,7 @@ fn rewrite_closure_fn_decl( .tactic(tactic) .preserve_newline(true); let list_str = write_list(&item_vec, &fmt)?; - let mut prefix = format!("{}{}{}|{}|", is_async, immovable, mover, list_str); + let mut prefix = format!("{}{}{}|{}|", immovable, is_async, mover, list_str); if !ret_str.is_empty() { if prefix.contains('\n') { diff --git a/src/config/file_lines.rs b/src/config/file_lines.rs index 4b799780d85d9..7b498dc46b320 100644 --- a/src/config/file_lines.rs +++ b/src/config/file_lines.rs @@ -13,9 +13,9 @@ use thiserror::Error; /// A range of lines in a file, inclusive of both ends. pub struct LineRange { - pub file: Lrc, - pub lo: usize, - pub hi: usize, + pub(crate) file: Lrc, + pub(crate) lo: usize, + pub(crate) hi: usize, } /// Defines the name of an input - either a file or stdin. @@ -75,7 +75,7 @@ impl Serialize for FileName { } impl LineRange { - pub fn file_name(&self) -> FileName { + pub(crate) fn file_name(&self) -> FileName { self.file.name.clone().into() } } diff --git a/src/config/options.rs b/src/config/options.rs index bce9e5d07f267..d857c29be29c6 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -218,24 +218,24 @@ pub enum Verbosity { pub struct WidthHeuristics { // Maximum width of the args of a function call before falling back // to vertical formatting. - pub fn_call_width: usize, + pub(crate) fn_call_width: usize, // Maximum width of the args of a function-like attributes before falling // back to vertical formatting. - pub attr_fn_like_width: usize, + pub(crate) attr_fn_like_width: usize, // Maximum width in the body of a struct lit before falling back to // vertical formatting. - pub struct_lit_width: usize, + pub(crate) struct_lit_width: usize, // Maximum width in the body of a struct variant before falling back // to vertical formatting. - pub struct_variant_width: usize, + pub(crate) struct_variant_width: usize, // Maximum width of an array literal before falling back to vertical // formatting. - pub array_width: usize, + pub(crate) array_width: usize, // Maximum length of a chain to fit on a single line. - pub chain_width: usize, + pub(crate) chain_width: usize, // Maximum line length for single line if-else expressions. A value // of zero means always break if-else expressions. - pub single_line_if_else_max_width: usize, + pub(crate) single_line_if_else_max_width: usize, } impl fmt::Display for WidthHeuristics { diff --git a/src/expr.rs b/src/expr.rs index 5fd86c1a4eadd..c9c8852cd3b56 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -108,9 +108,21 @@ pub(crate) fn format_expr( ast::ExprKind::Unary(op, ref subexpr) => rewrite_unary_op(context, op, subexpr, shape), ast::ExprKind::Struct(ref struct_expr) => { let ast::StructExpr { - fields, path, rest, .. + qself, + fields, + path, + rest, } = &**struct_expr; - rewrite_struct_lit(context, path, fields, rest, &expr.attrs, expr.span, shape) + rewrite_struct_lit( + context, + path, + qself.as_ref(), + fields, + rest, + &expr.attrs, + expr.span, + shape, + ) } ast::ExprKind::Tup(ref items) => { rewrite_tuple(context, items.iter(), expr.span, shape, items.len() == 1) @@ -1511,6 +1523,7 @@ fn struct_lit_can_be_aligned(fields: &[ast::ExprField], has_base: bool) -> bool fn rewrite_struct_lit<'a>( context: &RewriteContext<'_>, path: &ast::Path, + qself: Option<&ast::QSelf>, fields: &'a [ast::ExprField], struct_rest: &ast::StructRest, attrs: &[ast::Attribute], @@ -1527,7 +1540,7 @@ fn rewrite_struct_lit<'a>( // 2 = " {".len() let path_shape = shape.sub_width(2)?; - let path_str = rewrite_path(context, PathContext::Expr, None, path, path_shape)?; + 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)), @@ -2003,9 +2016,7 @@ fn choose_rhs( has_rhs_comment: bool, ) -> Option { match orig_rhs { - Some(ref new_str) if new_str.is_empty() => { - return Some(String::new()); - } + Some(ref new_str) if new_str.is_empty() => Some(String::new()), Some(ref new_str) if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => { diff --git a/src/formatting.rs b/src/formatting.rs index 7d0facb8f12cf..67cf1232f66ab 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -5,6 +5,7 @@ use std::io::{self, Write}; use std::time::{Duration, Instant}; use rustc_ast::ast; +use rustc_ast::AstLike; use rustc_span::Span; use self::newline_style::apply_newline_style; @@ -13,9 +14,9 @@ use crate::config::{Config, FileName, Verbosity}; use crate::formatting::generated::is_generated_file; use crate::issues::BadIssueSeeker; use crate::modules::Module; -use crate::syntux::parser::{DirectoryOwnership, Parser, ParserError}; -use crate::syntux::session::ParseSess; -use crate::utils::count_newlines; +use crate::parse::parser::{DirectoryOwnership, Parser, ParserError}; +use crate::parse::session::ParseSess; +use crate::utils::{contains_skip, count_newlines}; use crate::visitor::FmtVisitor; use crate::{modules, source_file, ErrorKind, FormatReport, Input, Session}; @@ -58,6 +59,39 @@ impl<'b, T: Write + 'b> Session<'b, T> { } } +/// Determine if a module should be skipped. True if the module should be skipped, false otherwise. +fn should_skip_module( + config: &Config, + context: &FormatContext<'_, T>, + input_is_stdin: bool, + main_file: &FileName, + path: &FileName, + module: &Module<'_>, +) -> bool { + if contains_skip(module.attrs()) { + return true; + } + + if config.skip_children() && path != main_file { + return true; + } + + if !input_is_stdin && context.ignore_file(path) { + return true; + } + + if !config.format_generated_files() { + let source_file = context.parse_session.span_to_file_contents(module.span); + let src = source_file.src.as_ref().expect("SourceFile without src"); + + if is_generated_file(src) { + return true; + } + } + + false +} + // Format an entire crate (or subset of the module tree). fn format_project( input: Input, @@ -97,7 +131,12 @@ fn format_project( directory_ownership.unwrap_or(DirectoryOwnership::UnownedViaBlock), !input_is_stdin && !config.skip_children(), ) - .visit_crate(&krate)?; + .visit_crate(&krate)? + .into_iter() + .filter(|(path, module)| { + !should_skip_module(config, &context, input_is_stdin, &main_file, path, module) + }) + .collect::>(); timer = timer.done_parsing(); @@ -105,15 +144,6 @@ fn format_project( context.parse_session.set_silent_emitter(); for (path, module) in files { - let source_file = context.parse_session.span_to_file_contents(module.span); - let src = source_file.src.as_ref().expect("SourceFile without src"); - - let should_ignore = (!input_is_stdin && context.ignore_file(&path)) - || (!config.format_generated_files() && is_generated_file(src)); - - if (config.skip_children() && path != main_file) || should_ignore { - continue; - } should_emit_verbose(input_is_stdin, config, || println!("Formatting {}", path)); context.format_file(path, &module, is_macro_def)?; } diff --git a/src/items.rs b/src/items.rs index b7dd6b06ff827..babc56f86edc6 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1535,7 +1535,7 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( // https://rustc-dev-guide.rust-lang.org/opaque-types-type-alias-impl-trait.html // https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/items.md#type-aliases match (visitor_kind, &op_ty) { - (Item(_) | AssocTraitItem(_) | ForeignItem(_), Some(ref op_bounds)) => { + (Item(_) | AssocTraitItem(_) | ForeignItem(_), Some(op_bounds)) => { let op = OpaqueType { bounds: op_bounds }; rewrite_ty(rw_info, Some(bounds), Some(&op), vis) } @@ -1543,7 +1543,7 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( rewrite_ty(rw_info, Some(bounds), ty_opt, vis) } (AssocImplItem(_), _) => { - let result = if let Some(ref op_bounds) = op_ty { + let result = if let Some(op_bounds) = op_ty { let op = OpaqueType { bounds: op_bounds }; rewrite_ty(rw_info, Some(bounds), Some(&op), &DEFAULT_VISIBILITY) } else { @@ -3124,7 +3124,7 @@ impl Rewrite for ast::ForeignItem { let inner_attrs = inner_attributes(&self.attrs); let fn_ctxt = visit::FnCtxt::Foreign; visitor.visit_fn( - visit::FnKind::Fn(fn_ctxt, self.ident, &sig, &self.vis, Some(body)), + visit::FnKind::Fn(fn_ctxt, self.ident, sig, &self.vis, Some(body)), generics, &sig.decl, self.span, @@ -3137,7 +3137,7 @@ impl Rewrite for ast::ForeignItem { context, shape.indent, self.ident, - &FnSig::from_method_sig(&sig, generics, &self.vis), + &FnSig::from_method_sig(sig, generics, &self.vis), span, FnBraceStyle::None, ) @@ -3166,7 +3166,7 @@ impl Rewrite for ast::ForeignItem { .map(|s| s + ";") } ast::ForeignItemKind::TyAlias(ref ty_alias) => { - let (kind, span) = (&ItemVisitorKind::ForeignItem(&self), self.span); + let (kind, span) = (&ItemVisitorKind::ForeignItem(self), self.span); rewrite_type_alias(ty_alias, context, shape.indent, kind, span) } ast::ForeignItemKind::MacCall(ref mac) => { diff --git a/src/lib.rs b/src/lib.rs index 792a1080f0e92..ad23b16e02ec1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ extern crate log; // N.B. these crates are loaded from the sysroot, so they need extern crate. extern crate rustc_ast; extern crate rustc_ast_pretty; +extern crate rustc_builtin_macros; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_expand; @@ -40,8 +41,8 @@ use crate::emitter::Emitter; use crate::formatting::{FormatErrorMap, FormattingError, ReportedErrors, SourceFile}; use crate::issues::Issue; use crate::modules::ModuleResolutionError; +use crate::parse::parser::DirectoryOwnership; use crate::shape::Indent; -use crate::syntux::parser::DirectoryOwnership; use crate::utils::indent_next_line; pub use crate::config::{ @@ -77,6 +78,7 @@ mod missed_spans; pub(crate) mod modules; mod overflow; mod pairs; +mod parse; mod patterns; mod release_channel; mod reorder; @@ -89,7 +91,6 @@ pub(crate) mod source_map; mod spanned; mod stmt; mod string; -mod syntux; #[cfg(test)] mod test; mod types; diff --git a/src/lists.rs b/src/lists.rs index 3515dd172510c..7aa0315f18c26 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -448,10 +448,8 @@ where true } else if starts_with_newline(comment) { false - } else if comment.trim().contains('\n') || comment.trim().len() > width { - true } else { - false + comment.trim().contains('\n') || comment.trim().len() > width }; rewrite_comment( diff --git a/src/macros.rs b/src/macros.rs index a52568be9eac4..f29552caf8d87 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -16,8 +16,6 @@ use rustc_ast::token::{BinOpToken, DelimToken, Token, TokenKind}; use rustc_ast::tokenstream::{Cursor, Spacing, TokenStream, TokenTree}; use rustc_ast::{ast, ptr}; use rustc_ast_pretty::pprust; -use rustc_parse::parser::{ForceCollect, Parser}; -use rustc_parse::{stream_to_parser, MACRO_ARGUMENTS}; use rustc_span::{ symbol::{self, kw}, BytePos, Span, Symbol, DUMMY_SP, @@ -30,6 +28,8 @@ use crate::config::lists::*; use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind}; use crate::lists::{itemize_list, write_list, ListFormatting}; use crate::overflow; +use crate::parse::macros::lazy_static::parse_lazy_static; +use crate::parse::macros::{parse_expr, parse_macro_args, ParsedMacroArgs}; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; use crate::source_map::SpanUtils; @@ -60,7 +60,7 @@ pub(crate) enum MacroArg { } impl MacroArg { - fn is_item(&self) -> bool { + pub(crate) fn is_item(&self) -> bool { match self { MacroArg::Item(..) => true, _ => false, @@ -90,61 +90,6 @@ impl Rewrite for MacroArg { } } -fn build_parser<'a>(context: &RewriteContext<'a>, cursor: Cursor) -> Parser<'a> { - stream_to_parser( - context.parse_sess.inner(), - cursor.collect(), - MACRO_ARGUMENTS, - ) -} - -fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { - macro_rules! parse_macro_arg { - ($macro_arg:ident, $parser:expr, $f:expr) => { - let mut cloned_parser = (*parser).clone(); - match $parser(&mut cloned_parser) { - Ok(x) => { - if parser.sess.span_diagnostic.has_errors() { - parser.sess.span_diagnostic.reset_err_count(); - } else { - // Parsing succeeded. - *parser = cloned_parser; - return Some(MacroArg::$macro_arg($f(x)?)); - } - } - Err(mut e) => { - e.cancel(); - parser.sess.span_diagnostic.reset_err_count(); - } - } - }; - } - - parse_macro_arg!( - Expr, - |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_expr(), - |x: ptr::P| Some(x) - ); - parse_macro_arg!( - Ty, - |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_ty(), - |x: ptr::P| Some(x) - ); - parse_macro_arg!( - Pat, - |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_pat_no_top_alt(None), - |x: ptr::P| Some(x) - ); - // `parse_item` returns `Option>`. - parse_macro_arg!( - Item, - |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_item(ForceCollect::No), - |x: Option>| x - ); - - None -} - /// Rewrite macro name without using pretty-printer if possible. fn rewrite_macro_name( context: &RewriteContext<'_>, @@ -232,25 +177,6 @@ pub(crate) fn rewrite_macro( } } -fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { - for &keyword in RUST_KW.iter() { - if parser.token.is_keyword(keyword) - && parser.look_ahead(1, |t| { - t.kind == TokenKind::Eof - || t.kind == TokenKind::Comma - || t.kind == TokenKind::CloseDelim(DelimToken::NoDelim) - }) - { - parser.bump(); - return Some(MacroArg::Keyword( - symbol::Ident::with_dummy_span(keyword), - parser.prev_token.span, - )); - } - } - None -} - fn rewrite_macro_inner( mac: &ast::MacCall, extra_ident: Option, @@ -269,8 +195,9 @@ fn rewrite_macro_inner( let original_style = macro_style(mac, context); let macro_name = rewrite_macro_name(context, &mac.path, extra_ident); + let is_forced_bracket = FORCED_BRACKET_MACROS.contains(&¯o_name[..]); - let style = if FORCED_BRACKET_MACROS.contains(&¯o_name[..]) && !is_nested_macro { + let style = if is_forced_bracket && !is_nested_macro { DelimToken::Bracket } else { original_style @@ -294,67 +221,21 @@ fn rewrite_macro_inner( } // Format well-known macros which cannot be parsed as a valid AST. if macro_name == "lazy_static!" && !has_comment { - if let success @ Some(..) = format_lazy_static(context, shape, &ts) { + if let success @ Some(..) = format_lazy_static(context, shape, ts.trees().collect()) { return success; } } - let mut parser = build_parser(context, ts.trees()); - let mut arg_vec = Vec::new(); - let mut vec_with_semi = false; - let mut trailing_comma = false; - - if DelimToken::Brace != style { - loop { - if let Some(arg) = check_keyword(&mut parser) { - arg_vec.push(arg); - } else if let Some(arg) = parse_macro_arg(&mut parser) { - arg_vec.push(arg); - } else { - return return_macro_parse_failure_fallback(context, shape.indent, mac.span()); - } - - match parser.token.kind { - TokenKind::Eof => break, - TokenKind::Comma => (), - TokenKind::Semi => { - // Try to parse `vec![expr; expr]` - if FORCED_BRACKET_MACROS.contains(&¯o_name[..]) { - parser.bump(); - if parser.token.kind != TokenKind::Eof { - match parse_macro_arg(&mut parser) { - Some(arg) => { - arg_vec.push(arg); - parser.bump(); - if parser.token.kind == TokenKind::Eof && arg_vec.len() == 2 { - vec_with_semi = true; - break; - } - } - None => { - return return_macro_parse_failure_fallback( - context, - shape.indent, - mac.span(), - ); - } - } - } - } - return return_macro_parse_failure_fallback(context, shape.indent, mac.span()); - } - _ if arg_vec.last().map_or(false, MacroArg::is_item) => continue, - _ => return return_macro_parse_failure_fallback(context, shape.indent, mac.span()), - } - - parser.bump(); - - if parser.token.kind == TokenKind::Eof { - trailing_comma = true; - break; - } + let ParsedMacroArgs { + args: arg_vec, + vec_with_semi, + trailing_comma, + } = match parse_macro_args(context, ts, style, is_forced_bracket) { + Some(args) => args, + None => { + return return_macro_parse_failure_fallback(context, shape.indent, mac.span()); } - } + }; if !arg_vec.is_empty() && arg_vec.iter().all(MacroArg::is_item) { return rewrite_macro_with_items( @@ -1179,11 +1060,10 @@ pub(crate) fn convert_try_mac( let path = &pprust::path_to_string(&mac.path); if path == "try" || path == "r#try" { let ts = mac.args.inner_tokens(); - let mut parser = build_parser(context, ts.trees()); Some(ast::Expr { id: ast::NodeId::root(), // dummy value - kind: ast::ExprKind::Try(parser.parse_expr().ok()?), + kind: ast::ExprKind::Try(parse_expr(context, ts)?), span: mac.span(), // incorrect span, but shouldn't matter too much attrs: ast::AttrVec::new(), tokens: None, @@ -1414,10 +1294,9 @@ impl MacroBranch { fn format_lazy_static( context: &RewriteContext<'_>, shape: Shape, - ts: &TokenStream, + ts: TokenStream, ) -> Option { let mut result = String::with_capacity(1024); - let mut parser = build_parser(context, ts.trees()); let nested_shape = shape .block_indent(context.config.tab_spaces()) .with_max_width(context.config); @@ -1425,42 +1304,11 @@ fn format_lazy_static( result.push_str("lazy_static! {"); result.push_str(&nested_shape.indent.to_string_with_newline(context.config)); - macro_rules! parse_or { - ($method:ident $(,)* $($arg:expr),* $(,)*) => { - match parser.$method($($arg,)*) { - Ok(val) => { - if parser.sess.span_diagnostic.has_errors() { - parser.sess.span_diagnostic.reset_err_count(); - return None; - } else { - val - } - } - Err(mut err) => { - err.cancel(); - parser.sess.span_diagnostic.reset_err_count(); - return None; - } - } - } - } - - while parser.token.kind != TokenKind::Eof { - // Parse a `lazy_static!` item. - let vis = crate::utils::format_visibility( - context, - &parse_or!(parse_visibility, rustc_parse::parser::FollowedByType::No), - ); - parser.eat_keyword(kw::Static); - parser.eat_keyword(kw::Ref); - let id = parse_or!(parse_ident); - parser.eat(&TokenKind::Colon); - let ty = parse_or!(parse_ty); - parser.eat(&TokenKind::Eq); - let expr = parse_or!(parse_expr); - parser.eat(&TokenKind::Semi); - + let parsed_elems = parse_lazy_static(context, ts)?; + let last = parsed_elems.len() - 1; + for (i, (vis, id, ty, expr)) in parsed_elems.iter().enumerate() { // Rewrite as a static item. + let vis = crate::utils::format_visibility(context, vis); let mut stmt = String::with_capacity(128); stmt.push_str(&format!( "{}static ref {}: {} =", @@ -1476,7 +1324,7 @@ fn format_lazy_static( nested_shape.sub_width(1)?, )?); result.push(';'); - if parser.token.kind != TokenKind::Eof { + if i != last { result.push_str(&nested_shape.indent.to_string_with_newline(context.config)); } } @@ -1528,65 +1376,3 @@ fn rewrite_macro_with_items( result.push_str(trailing_semicolon); Some(result) } - -const RUST_KW: [Symbol; 59] = [ - kw::PathRoot, - kw::DollarCrate, - kw::Underscore, - kw::As, - kw::Box, - kw::Break, - kw::Const, - kw::Continue, - kw::Crate, - kw::Else, - kw::Enum, - kw::Extern, - kw::False, - kw::Fn, - kw::For, - kw::If, - kw::Impl, - kw::In, - kw::Let, - kw::Loop, - kw::Match, - kw::Mod, - kw::Move, - kw::Mut, - kw::Pub, - kw::Ref, - kw::Return, - kw::SelfLower, - kw::SelfUpper, - kw::Static, - kw::Struct, - kw::Super, - kw::Trait, - kw::True, - kw::Type, - kw::Unsafe, - kw::Use, - kw::Where, - kw::While, - kw::Abstract, - kw::Become, - kw::Do, - kw::Final, - kw::Macro, - kw::Override, - kw::Priv, - kw::Typeof, - kw::Unsized, - kw::Virtual, - kw::Yield, - kw::Dyn, - kw::Async, - kw::Try, - kw::UnderscoreLifetime, - kw::StaticLifetime, - kw::Auto, - kw::Catch, - kw::Default, - kw::Union, -]; diff --git a/src/matches.rs b/src/matches.rs index 22d23fc1cdba4..85d9c5d2b9bbf 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -322,7 +322,11 @@ fn flatten_arm_body<'a>( if let Some(block) = block_can_be_flattened(context, body) { if let ast::StmtKind::Expr(ref expr) = block.stmts[0].kind { if let ast::ExprKind::Block(..) = expr.kind { - flatten_arm_body(context, expr, None) + if expr.attrs.is_empty() { + flatten_arm_body(context, expr, None) + } else { + (true, body) + } } else { let cond_becomes_muti_line = opt_shape .and_then(|shape| rewrite_cond(context, expr, shape)) diff --git a/src/modules.rs b/src/modules.rs index 9d438a80d942f..9c964b274e088 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -12,10 +12,10 @@ use thiserror::Error; use crate::attr::MetaVisitor; use crate::config::FileName; use crate::items::is_mod_decl; -use crate::syntux::parser::{ +use crate::parse::parser::{ Directory, DirectoryOwnership, ModError, ModulePathSuccess, Parser, ParserError, }; -use crate::syntux::session::ParseSess; +use crate::parse::session::ParseSess; use crate::utils::{contains_skip, mk_sp}; mod visitor; diff --git a/src/modules/visitor.rs b/src/modules/visitor.rs index d5acf3f1cbcb4..ea67977c17a2b 100644 --- a/src/modules/visitor.rs +++ b/src/modules/visitor.rs @@ -3,8 +3,8 @@ use rustc_ast::visit::Visitor; use rustc_span::Symbol; use crate::attr::MetaVisitor; -use crate::syntux::parser::Parser; -use crate::syntux::session::ParseSess; +use crate::parse::macros::cfg_if::parse_cfg_if; +use crate::parse::session::ParseSess; pub(crate) struct ModItem { pub(crate) item: ast::Item, @@ -62,7 +62,7 @@ impl<'a, 'ast: 'a> CfgIfVisitor<'a> { } }; - let items = Parser::parse_cfg_if(self.parse_sess, mac)?; + let items = parse_cfg_if(self.parse_sess, mac)?; self.mods .append(&mut items.into_iter().map(|item| ModItem { item }).collect()); diff --git a/src/parse/macros/asm.rs b/src/parse/macros/asm.rs new file mode 100644 index 0000000000000..cc9fb5072ce1e --- /dev/null +++ b/src/parse/macros/asm.rs @@ -0,0 +1,11 @@ +use rustc_ast::ast; +use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs}; + +use crate::rewrite::RewriteContext; + +#[allow(dead_code)] +pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option { + let ts = mac.args.inner_tokens(); + let mut parser = super::build_parser(context, ts); + parse_asm_args(&mut parser, context.parse_sess.inner(), mac.span(), false).ok() +} diff --git a/src/parse/macros/cfg_if.rs b/src/parse/macros/cfg_if.rs new file mode 100644 index 0000000000000..e10fbe64bcdbe --- /dev/null +++ b/src/parse/macros/cfg_if.rs @@ -0,0 +1,89 @@ +use std::panic::{catch_unwind, AssertUnwindSafe}; + +use rustc_ast::ast; +use rustc_ast::token::{DelimToken, TokenKind}; +use rustc_parse::parser::ForceCollect; +use rustc_span::symbol::kw; + +use crate::parse::macros::build_stream_parser; +use crate::parse::session::ParseSess; + +pub(crate) fn parse_cfg_if<'a>( + sess: &'a ParseSess, + mac: &'a ast::MacCall, +) -> Result, &'static str> { + match catch_unwind(AssertUnwindSafe(|| parse_cfg_if_inner(sess, mac))) { + Ok(Ok(items)) => Ok(items), + Ok(err @ Err(_)) => err, + Err(..) => Err("failed to parse cfg_if!"), + } +} + +fn parse_cfg_if_inner<'a>( + sess: &'a ParseSess, + mac: &'a ast::MacCall, +) -> Result, &'static str> { + let ts = mac.args.inner_tokens(); + let mut parser = build_stream_parser(sess.inner(), ts); + + let mut items = vec![]; + let mut process_if_cfg = true; + + while parser.token.kind != TokenKind::Eof { + if process_if_cfg { + if !parser.eat_keyword(kw::If) { + return Err("Expected `if`"); + } + // Inner attributes are not actually syntactically permitted here, but we don't + // care about inner vs outer attributes in this position. Our purpose with this + // special case parsing of cfg_if macros is to ensure we can correctly resolve + // imported modules that may have a custom `path` defined. + // + // As such, we just need to advance the parser past the attribute and up to + // to the opening brace. + // See also https://github.com/rust-lang/rust/pull/79433 + parser + .parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted) + .map_err(|_| "Failed to parse attributes")?; + } + + if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) { + return Err("Expected an opening brace"); + } + + while parser.token != TokenKind::CloseDelim(DelimToken::Brace) + && parser.token.kind != TokenKind::Eof + { + let item = match parser.parse_item(ForceCollect::No) { + Ok(Some(item_ptr)) => item_ptr.into_inner(), + Ok(None) => continue, + Err(mut err) => { + err.cancel(); + parser.sess.span_diagnostic.reset_err_count(); + return Err( + "Expected item inside cfg_if block, but failed to parse it as an item", + ); + } + }; + if let ast::ItemKind::Mod(..) = item.kind { + items.push(item); + } + } + + if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) { + return Err("Expected a closing brace"); + } + + if parser.eat(&TokenKind::Eof) { + break; + } + + if !parser.eat_keyword(kw::Else) { + return Err("Expected `else`"); + } + + process_if_cfg = parser.token.is_keyword(kw::If); + } + + Ok(items) +} diff --git a/src/parse/macros/lazy_static.rs b/src/parse/macros/lazy_static.rs new file mode 100644 index 0000000000000..9c8651aa3faf7 --- /dev/null +++ b/src/parse/macros/lazy_static.rs @@ -0,0 +1,50 @@ +use rustc_ast::ast; +use rustc_ast::ptr::P; +use rustc_ast::token::TokenKind; +use rustc_ast::tokenstream::TokenStream; +use rustc_span::symbol::{self, kw}; + +use crate::rewrite::RewriteContext; + +pub(crate) fn parse_lazy_static( + context: &RewriteContext<'_>, + ts: TokenStream, +) -> Option, P)>> { + let mut result = vec![]; + let mut parser = super::build_parser(context, ts); + macro_rules! parse_or { + ($method:ident $(,)* $($arg:expr),* $(,)*) => { + match parser.$method($($arg,)*) { + Ok(val) => { + if parser.sess.span_diagnostic.has_errors() { + parser.sess.span_diagnostic.reset_err_count(); + return None; + } else { + val + } + } + Err(mut err) => { + err.cancel(); + parser.sess.span_diagnostic.reset_err_count(); + return None; + } + } + } + } + + while parser.token.kind != TokenKind::Eof { + // Parse a `lazy_static!` item. + let vis = parse_or!(parse_visibility, rustc_parse::parser::FollowedByType::No); + parser.eat_keyword(kw::Static); + parser.eat_keyword(kw::Ref); + let id = parse_or!(parse_ident); + parser.eat(&TokenKind::Colon); + let ty = parse_or!(parse_ty); + parser.eat(&TokenKind::Eq); + let expr = parse_or!(parse_expr); + parser.eat(&TokenKind::Semi); + result.push((vis, id, ty, expr)); + } + + Some(result) +} diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs new file mode 100644 index 0000000000000..2e9ce1d35f400 --- /dev/null +++ b/src/parse/macros/mod.rs @@ -0,0 +1,231 @@ +use rustc_ast::token::{DelimToken, TokenKind}; +use rustc_ast::tokenstream::TokenStream; +use rustc_ast::{ast, ptr}; +use rustc_parse::parser::{ForceCollect, Parser}; +use rustc_parse::{stream_to_parser, MACRO_ARGUMENTS}; +use rustc_session::parse::ParseSess; +use rustc_span::symbol::{self, kw}; +use rustc_span::Symbol; + +use crate::macros::MacroArg; +use crate::rewrite::RewriteContext; + +pub(crate) mod asm; +pub(crate) mod cfg_if; +pub(crate) mod lazy_static; + +fn build_stream_parser<'a>(sess: &'a ParseSess, tokens: TokenStream) -> Parser<'a> { + stream_to_parser(sess, tokens, MACRO_ARGUMENTS) +} + +fn build_parser<'a>(context: &RewriteContext<'a>, tokens: TokenStream) -> Parser<'a> { + build_stream_parser(context.parse_sess.inner(), tokens) +} + +fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { + macro_rules! parse_macro_arg { + ($macro_arg:ident, $parser:expr, $f:expr) => { + let mut cloned_parser = (*parser).clone(); + match $parser(&mut cloned_parser) { + Ok(x) => { + if parser.sess.span_diagnostic.has_errors() { + parser.sess.span_diagnostic.reset_err_count(); + } else { + // Parsing succeeded. + *parser = cloned_parser; + return Some(MacroArg::$macro_arg($f(x)?)); + } + } + Err(mut e) => { + e.cancel(); + parser.sess.span_diagnostic.reset_err_count(); + } + } + }; + } + + parse_macro_arg!( + Expr, + |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_expr(), + |x: ptr::P| Some(x) + ); + parse_macro_arg!( + Ty, + |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_ty(), + |x: ptr::P| Some(x) + ); + parse_macro_arg!( + Pat, + |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_pat_no_top_alt(None), + |x: ptr::P| Some(x) + ); + // `parse_item` returns `Option>`. + parse_macro_arg!( + Item, + |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_item(ForceCollect::No), + |x: Option>| x + ); + + None +} + +pub(crate) struct ParsedMacroArgs { + pub(crate) vec_with_semi: bool, + pub(crate) trailing_comma: bool, + pub(crate) args: Vec, +} + +fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { + for &keyword in RUST_KW.iter() { + if parser.token.is_keyword(keyword) + && parser.look_ahead(1, |t| { + t.kind == TokenKind::Eof + || t.kind == TokenKind::Comma + || t.kind == TokenKind::CloseDelim(DelimToken::NoDelim) + }) + { + parser.bump(); + return Some(MacroArg::Keyword( + symbol::Ident::with_dummy_span(keyword), + parser.prev_token.span, + )); + } + } + None +} + +pub(crate) fn parse_macro_args( + context: &RewriteContext<'_>, + tokens: TokenStream, + style: DelimToken, + forced_bracket: bool, +) -> Option { + let mut parser = build_parser(context, tokens); + let mut args = Vec::new(); + let mut vec_with_semi = false; + let mut trailing_comma = false; + + if DelimToken::Brace != style { + loop { + if let Some(arg) = check_keyword(&mut parser) { + args.push(arg); + } else if let Some(arg) = parse_macro_arg(&mut parser) { + args.push(arg); + } else { + return None; + } + + match parser.token.kind { + TokenKind::Eof => break, + TokenKind::Comma => (), + TokenKind::Semi => { + // Try to parse `vec![expr; expr]` + if forced_bracket { + parser.bump(); + if parser.token.kind != TokenKind::Eof { + match parse_macro_arg(&mut parser) { + Some(arg) => { + args.push(arg); + parser.bump(); + if parser.token.kind == TokenKind::Eof && args.len() == 2 { + vec_with_semi = true; + break; + } + } + None => { + return None; + } + } + } + } + return None; + } + _ if args.last().map_or(false, MacroArg::is_item) => continue, + _ => return None, + } + + parser.bump(); + + if parser.token.kind == TokenKind::Eof { + trailing_comma = true; + break; + } + } + } + + Some(ParsedMacroArgs { + vec_with_semi, + trailing_comma, + args, + }) +} + +pub(crate) fn parse_expr( + context: &RewriteContext<'_>, + tokens: TokenStream, +) -> Option> { + let mut parser = build_parser(context, tokens); + parser.parse_expr().ok() +} + +const RUST_KW: [Symbol; 59] = [ + kw::PathRoot, + kw::DollarCrate, + kw::Underscore, + kw::As, + kw::Box, + kw::Break, + kw::Const, + kw::Continue, + kw::Crate, + kw::Else, + kw::Enum, + kw::Extern, + kw::False, + kw::Fn, + kw::For, + kw::If, + kw::Impl, + kw::In, + kw::Let, + kw::Loop, + kw::Match, + kw::Mod, + kw::Move, + kw::Mut, + kw::Pub, + kw::Ref, + kw::Return, + kw::SelfLower, + kw::SelfUpper, + kw::Static, + kw::Struct, + kw::Super, + kw::Trait, + kw::True, + kw::Type, + kw::Unsafe, + kw::Use, + kw::Where, + kw::While, + kw::Abstract, + kw::Become, + kw::Do, + kw::Final, + kw::Macro, + kw::Override, + kw::Priv, + kw::Typeof, + kw::Unsized, + kw::Virtual, + kw::Yield, + kw::Dyn, + kw::Async, + kw::Try, + kw::UnderscoreLifetime, + kw::StaticLifetime, + kw::Auto, + kw::Catch, + kw::Default, + kw::Union, +]; diff --git a/src/parse/mod.rs b/src/parse/mod.rs new file mode 100644 index 0000000000000..5e88826ea8cc5 --- /dev/null +++ b/src/parse/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod macros; +pub(crate) mod parser; +pub(crate) mod session; diff --git a/src/syntux/parser.rs b/src/parse/parser.rs similarity index 61% rename from src/syntux/parser.rs rename to src/parse/parser.rs index 23d065c9cc95a..657217633f4ab 100644 --- a/src/syntux/parser.rs +++ b/src/parse/parser.rs @@ -1,17 +1,14 @@ use std::panic::{catch_unwind, AssertUnwindSafe}; use std::path::{Path, PathBuf}; -use rustc_ast::token::{DelimToken, TokenKind}; +use rustc_ast::token::TokenKind; use rustc_ast::{ast, ptr}; use rustc_errors::Diagnostic; -use rustc_parse::{ - new_parser_from_file, - parser::{ForceCollect, Parser as RawParser}, -}; -use rustc_span::{sym, symbol::kw, Span}; +use rustc_parse::{new_parser_from_file, parser::Parser as RawParser}; +use rustc_span::{sym, Span}; use crate::attr::first_attr_value_str_by_name; -use crate::syntux::session::ParseSess; +use crate::parse::session::ParseSess; use crate::Input; pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership; @@ -175,84 +172,4 @@ impl<'a> Parser<'a> { Err(_) => Err(ParserError::ParsePanicError), } } - - pub(crate) fn parse_cfg_if( - sess: &'a ParseSess, - mac: &'a ast::MacCall, - ) -> Result, &'static str> { - match catch_unwind(AssertUnwindSafe(|| Parser::parse_cfg_if_inner(sess, mac))) { - Ok(Ok(items)) => Ok(items), - Ok(err @ Err(_)) => err, - Err(..) => Err("failed to parse cfg_if!"), - } - } - - fn parse_cfg_if_inner( - sess: &'a ParseSess, - mac: &'a ast::MacCall, - ) -> Result, &'static str> { - let token_stream = mac.args.inner_tokens(); - let mut parser = rustc_parse::stream_to_parser(sess.inner(), token_stream, Some("")); - - let mut items = vec![]; - let mut process_if_cfg = true; - - while parser.token.kind != TokenKind::Eof { - if process_if_cfg { - if !parser.eat_keyword(kw::If) { - return Err("Expected `if`"); - } - // Inner attributes are not actually syntactically permitted here, but we don't - // care about inner vs outer attributes in this position. Our purpose with this - // special case parsing of cfg_if macros is to ensure we can correctly resolve - // imported modules that may have a custom `path` defined. - // - // As such, we just need to advance the parser past the attribute and up to - // to the opening brace. - // See also https://github.com/rust-lang/rust/pull/79433 - parser - .parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted) - .map_err(|_| "Failed to parse attributes")?; - } - - if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) { - return Err("Expected an opening brace"); - } - - while parser.token != TokenKind::CloseDelim(DelimToken::Brace) - && parser.token.kind != TokenKind::Eof - { - let item = match parser.parse_item(ForceCollect::No) { - Ok(Some(item_ptr)) => item_ptr.into_inner(), - Ok(None) => continue, - Err(mut err) => { - err.cancel(); - parser.sess.span_diagnostic.reset_err_count(); - return Err( - "Expected item inside cfg_if block, but failed to parse it as an item", - ); - } - }; - if let ast::ItemKind::Mod(..) = item.kind { - items.push(item); - } - } - - if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) { - return Err("Expected a closing brace"); - } - - if parser.eat(&TokenKind::Eof) { - break; - } - - if !parser.eat_keyword(kw::Else) { - return Err("Expected `else`"); - } - - process_if_cfg = parser.token.is_keyword(kw::If); - } - - Ok(items) - } } diff --git a/src/syntux/session.rs b/src/parse/session.rs similarity index 99% rename from src/syntux/session.rs rename to src/parse/session.rs index dd7c7352686e6..624fed0d2de26 100644 --- a/src/syntux/session.rs +++ b/src/parse/session.rs @@ -222,7 +222,7 @@ impl ParseSess { } } -// Methods that should be restricted within the syntux module. +// Methods that should be restricted within the parse module. impl ParseSess { pub(super) fn emit_diagnostics(&self, diagnostics: Vec) { for diagnostic in diagnostics { diff --git a/src/patterns.rs b/src/patterns.rs index a80d63201f982..9b74b35f31413 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -318,10 +318,12 @@ fn rewrite_struct_pat( let mut fields_str = write_list(&item_vec, &fmt)?; let one_line_width = h_shape.map_or(0, |shape| shape.width); + let has_trailing_comma = fmt.needs_trailing_separator(); + if ellipsis { if fields_str.contains('\n') || fields_str.len() > one_line_width { // Add a missing trailing comma. - if context.config.trailing_comma() == SeparatorTactic::Never { + if !has_trailing_comma { fields_str.push(','); } fields_str.push('\n'); @@ -329,8 +331,7 @@ fn rewrite_struct_pat( } else { if !fields_str.is_empty() { // there are preceding struct fields being matched on - if tactic == DefinitiveListTactic::Vertical { - // if the tactic is Vertical, write_list already added a trailing , + if has_trailing_comma { fields_str.push(' '); } else { fields_str.push_str(", "); diff --git a/src/rewrite.rs b/src/rewrite.rs index c8abe70141b5c..4a3bd129d16f5 100644 --- a/src/rewrite.rs +++ b/src/rewrite.rs @@ -7,9 +7,9 @@ use rustc_ast::ptr; use rustc_span::Span; use crate::config::{Config, IndentStyle}; +use crate::parse::session::ParseSess; use crate::shape::Shape; use crate::skip::SkipContext; -use crate::syntux::session::ParseSess; use crate::visitor::SnippetProvider; use crate::FormatReport; diff --git a/src/rustfmt_diff.rs b/src/rustfmt_diff.rs index a394ce07398ef..1724a0f87bf7c 100644 --- a/src/rustfmt_diff.rs +++ b/src/rustfmt_diff.rs @@ -6,20 +6,20 @@ use std::io::Write; use crate::config::{Color, Config, Verbosity}; #[derive(Debug, PartialEq)] -pub enum DiffLine { +pub(crate) enum DiffLine { Context(String), Expected(String), Resulting(String), } #[derive(Debug, PartialEq)] -pub struct Mismatch { +pub(crate) struct Mismatch { /// The line number in the formatted version. - pub line_number: u32, + pub(crate) line_number: u32, /// The line number in the original version. - pub line_number_orig: u32, + pub(crate) line_number_orig: u32, /// The set of lines (context and old/new) in the mismatch. - pub lines: Vec, + pub(crate) lines: Vec, } impl Mismatch { diff --git a/src/source_file.rs b/src/source_file.rs index 853336004d8b1..56d4ab4003832 100644 --- a/src/source_file.rs +++ b/src/source_file.rs @@ -4,7 +4,7 @@ use std::path::Path; use crate::config::FileName; use crate::emitter::{self, Emitter}; -use crate::syntux::session::ParseSess; +use crate::parse::session::ParseSess; use crate::NewlineStyle; #[cfg(test)] diff --git a/src/test/configuration_snippet.rs b/src/test/configuration_snippet.rs index ef7dd0ddcd123..92949ab576a6b 100644 --- a/src/test/configuration_snippet.rs +++ b/src/test/configuration_snippet.rs @@ -110,14 +110,7 @@ impl ConfigCodeBlock { assert!(self.code_block.is_some() && self.code_block_start.is_some()); // See if code block begins with #![rustfmt::skip]. - let fmt_skip = self - .code_block - .as_ref() - .unwrap() - .lines() - .nth(0) - .unwrap_or("") - == "#![rustfmt::skip]"; + let fmt_skip = self.fmt_skip(); if self.config_name.is_none() && !fmt_skip { write_message(&format!( @@ -138,6 +131,17 @@ impl ConfigCodeBlock { true } + /// True if the code block starts with #![rustfmt::skip] + fn fmt_skip(&self) -> bool { + self.code_block + .as_ref() + .unwrap() + .lines() + .nth(0) + .unwrap_or("") + == "#![rustfmt::skip]" + } + fn has_parsing_errors(&self, session: &Session<'_, T>) -> bool { if session.has_parsing_errors() { write_message(&format!( @@ -251,6 +255,7 @@ fn configuration_snippet_tests() { let blocks = get_code_blocks(); let failures = blocks .iter() + .filter(|block| !block.fmt_skip()) .map(ConfigCodeBlock::formatted_is_idempotent) .fold(0, |acc, r| acc + (!r as u32)); diff --git a/src/test/mod_resolver.rs b/src/test/mod_resolver.rs index ae4a0d0fccb19..ec9ed0f0b8d62 100644 --- a/src/test/mod_resolver.rs +++ b/src/test/mod_resolver.rs @@ -41,3 +41,12 @@ fn out_of_line_nested_inline_within_out_of_line() { ], ); } + +#[test] +fn skip_out_of_line_nested_inline_within_out_of_line() { + // See also https://github.com/rust-lang/rustfmt/issues/5065 + verify_mod_resolution( + "tests/mod-resolver/skip-files-issue-5065/main.rs", + &["tests/mod-resolver/skip-files-issue-5065/one.rs"], + ); +} diff --git a/src/visitor.rs b/src/visitor.rs index e4a7be742abcb..0177689958aa7 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -16,13 +16,13 @@ use crate::items::{ }; use crate::macros::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition}; use crate::modules::Module; +use crate::parse::session::ParseSess; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; use crate::skip::{is_skip_attr, SkipContext}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; use crate::stmt::Stmt; -use crate::syntux::session::ParseSess; use crate::utils::{ self, contains_skip, count_newlines, depr_skip_annotation, format_unsafety, inner_attributes, last_line_width, mk_sp, ptr_vec_to_ref_vec, rewrite_ident, starts_with_newline, stmt_expr, @@ -552,7 +552,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { _ => visit::FnCtxt::Foreign, }; self.visit_fn( - visit::FnKind::Fn(fn_ctxt, item.ident, &sig, &item.vis, Some(body)), + visit::FnKind::Fn(fn_ctxt, item.ident, sig, &item.vis, Some(body)), generics, &sig.decl, item.span, @@ -562,14 +562,14 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { } else { let indent = self.block_indent; let rewrite = self.rewrite_required_fn( - indent, item.ident, &sig, &item.vis, generics, item.span, + indent, item.ident, sig, &item.vis, generics, item.span, ); self.push_rewrite(item.span, rewrite); } } ast::ItemKind::TyAlias(ref ty_alias) => { use ItemVisitorKind::Item; - self.visit_ty_alias_kind(ty_alias, &Item(&item), item.span); + self.visit_ty_alias_kind(ty_alias, &Item(item), item.span); } ast::ItemKind::GlobalAsm(..) => { let snippet = Some(self.snippet(item.span).to_owned()); @@ -619,17 +619,17 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { skip_out_of_file_lines_range_visitor!(self, ai.span); if self.visit_attrs(&ai.attrs, ast::AttrStyle::Outer) { - self.push_skipped_with_span(&ai.attrs.as_slice(), skip_span, skip_span); + self.push_skipped_with_span(ai.attrs.as_slice(), skip_span, skip_span); return; } // TODO(calebcartwright): consider enabling box_patterns feature gate match (&ai.kind, visitor_kind) { (ast::AssocItemKind::Const(..), AssocTraitItem(_)) => { - self.visit_static(&StaticParts::from_trait_item(&ai)) + self.visit_static(&StaticParts::from_trait_item(ai)) } (ast::AssocItemKind::Const(..), AssocImplItem(_)) => { - self.visit_static(&StaticParts::from_impl_item(&ai)) + self.visit_static(&StaticParts::from_impl_item(ai)) } (ast::AssocItemKind::Fn(ref fn_kind), _) => { let ast::Fn { @@ -948,12 +948,13 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { pub(crate) fn format_separate_mod(&mut self, m: &Module<'_>, end_pos: BytePos) { self.block_indent = Indent::empty(); - if self.visit_attrs(m.attrs(), ast::AttrStyle::Inner) { - self.push_skipped_with_span(m.attrs(), m.span, m.span); - } else { - self.walk_mod_items(&m.items); - self.format_missing_with_indent(end_pos); - } + let skipped = self.visit_attrs(m.attrs(), ast::AttrStyle::Inner); + assert!( + !skipped, + "Skipping module must be handled before reaching this line." + ); + self.walk_mod_items(&m.items); + self.format_missing_with_indent(end_pos); } pub(crate) fn skip_empty_lines(&mut self, end_pos: BytePos) { diff --git a/tests/mod-resolver/skip-files-issue-5065/foo.rs b/tests/mod-resolver/skip-files-issue-5065/foo.rs new file mode 100644 index 0000000000000..74889acf0c38a --- /dev/null +++ b/tests/mod-resolver/skip-files-issue-5065/foo.rs @@ -0,0 +1,5 @@ +#![rustfmt::skip] + +mod bar { + + mod baz;} \ No newline at end of file diff --git a/tests/mod-resolver/skip-files-issue-5065/foo/bar/baz.rs b/tests/mod-resolver/skip-files-issue-5065/foo/bar/baz.rs new file mode 100644 index 0000000000000..3519b0ee59c88 --- /dev/null +++ b/tests/mod-resolver/skip-files-issue-5065/foo/bar/baz.rs @@ -0,0 +1 @@ +fn baz() { } \ No newline at end of file diff --git a/tests/mod-resolver/skip-files-issue-5065/main.rs b/tests/mod-resolver/skip-files-issue-5065/main.rs new file mode 100644 index 0000000000000..3122e4f220f62 --- /dev/null +++ b/tests/mod-resolver/skip-files-issue-5065/main.rs @@ -0,0 +1,9 @@ +#![rustfmt::skip] + +mod foo; +mod one; + +fn main() {println!("Hello, world!"); +} + +// trailing commet diff --git a/tests/mod-resolver/skip-files-issue-5065/one.rs b/tests/mod-resolver/skip-files-issue-5065/one.rs new file mode 100644 index 0000000000000..e7eb2c2d64dda --- /dev/null +++ b/tests/mod-resolver/skip-files-issue-5065/one.rs @@ -0,0 +1 @@ +struct One { value: String } \ No newline at end of file diff --git a/tests/source/async_block.rs b/tests/source/async_block.rs index 3de51a084d260..18cb4fb5f5cc5 100644 --- a/tests/source/async_block.rs +++ b/tests/source/async_block.rs @@ -32,4 +32,20 @@ fn baz() { Ok(()) }, ); + + spawn( + a, + static async || { + action(); + Ok(()) + }, + ); + + spawn( + a, + static async move || { + action(); + Ok(()) + }, + ); } diff --git a/tests/source/match.rs b/tests/source/match.rs index da20517203a65..b5dc9957a2caa 100644 --- a/tests/source/match.rs +++ b/tests/source/match.rs @@ -568,3 +568,22 @@ fn issue_3774() { } } } + +// #4109 +fn issue_4109() { + match () { + _ => { +#[cfg(debug_assertions)] +{ +println!("Foo"); +} +} +} + +match () { +_ => { +#[allow(unsafe_code)] +unsafe {} +} +} +} diff --git a/tests/target/async_block.rs b/tests/target/async_block.rs index 258dd937a6f56..137d849c9ee02 100644 --- a/tests/target/async_block.rs +++ b/tests/target/async_block.rs @@ -22,4 +22,14 @@ fn baz() { action(); Ok(()) }); + + spawn(a, static async || { + action(); + Ok(()) + }); + + spawn(a, static async move || { + action(); + Ok(()) + }); } diff --git a/tests/target/issue-5066/multi_line_struct_trailing_comma_always_struct_lit_width_0.rs b/tests/target/issue-5066/multi_line_struct_trailing_comma_always_struct_lit_width_0.rs new file mode 100644 index 0000000000000..c7122c676237e --- /dev/null +++ b/tests/target/issue-5066/multi_line_struct_trailing_comma_always_struct_lit_width_0.rs @@ -0,0 +1,10 @@ +// rustfmt-trailing_comma: Always +// rustfmt-struct_lit_single_line: false +// rustfmt-struct_lit_width: 0 + +fn main() { + let Foo { + a, + .. + } = b; +} diff --git a/tests/target/issue-5066/multi_line_struct_trailing_comma_never_struct_lit_width_0.rs b/tests/target/issue-5066/multi_line_struct_trailing_comma_never_struct_lit_width_0.rs new file mode 100644 index 0000000000000..68e89c4179f7d --- /dev/null +++ b/tests/target/issue-5066/multi_line_struct_trailing_comma_never_struct_lit_width_0.rs @@ -0,0 +1,10 @@ +// rustfmt-trailing_comma: Never +// rustfmt-struct_lit_single_line: false +// rustfmt-struct_lit_width: 0 + +fn main() { + let Foo { + a, + .. + } = b; +} diff --git a/tests/target/issue-5066/multi_line_struct_with_trailing_comma_always.rs b/tests/target/issue-5066/multi_line_struct_with_trailing_comma_always.rs new file mode 100644 index 0000000000000..3368f07038684 --- /dev/null +++ b/tests/target/issue-5066/multi_line_struct_with_trailing_comma_always.rs @@ -0,0 +1,10 @@ +// rustfmt-trailing_comma: Always +// rustfmt-struct_lit_single_line: false + +// There is an issue with how this is formatted. +// formatting should look like ./multi_line_struct_trailing_comma_always_struct_lit_width_0.rs +fn main() { + let Foo { + a, .. + } = b; +} diff --git a/tests/target/issue-5066/multi_line_struct_with_trailing_comma_never.rs b/tests/target/issue-5066/multi_line_struct_with_trailing_comma_never.rs new file mode 100644 index 0000000000000..cf63c4c983c46 --- /dev/null +++ b/tests/target/issue-5066/multi_line_struct_with_trailing_comma_never.rs @@ -0,0 +1,10 @@ +// rustfmt-trailing_comma: Never +// rustfmt-struct_lit_single_line: false + +// There is an issue with how this is formatted. +// formatting should look like ./multi_line_struct_trailing_comma_never_struct_lit_width_0.rs +fn main() { + let Foo { + a, .. + } = b; +} diff --git a/tests/target/issue-5066/with_trailing_comma_always.rs b/tests/target/issue-5066/with_trailing_comma_always.rs new file mode 100644 index 0000000000000..e20bcec931696 --- /dev/null +++ b/tests/target/issue-5066/with_trailing_comma_always.rs @@ -0,0 +1,5 @@ +// rustfmt-trailing_comma: Always + +fn main() { + let Foo { a, .. } = b; +} diff --git a/tests/target/issue-5066/with_trailing_comma_never.rs b/tests/target/issue-5066/with_trailing_comma_never.rs new file mode 100644 index 0000000000000..8b95bb137bca3 --- /dev/null +++ b/tests/target/issue-5066/with_trailing_comma_never.rs @@ -0,0 +1,5 @@ +// rustfmt-trailing_comma: Never + +fn main() { + let Foo { a, .. } = b; +} diff --git a/tests/target/issue-5151/minimum_example.rs b/tests/target/issue-5151/minimum_example.rs new file mode 100644 index 0000000000000..2ed3d936e32ae --- /dev/null +++ b/tests/target/issue-5151/minimum_example.rs @@ -0,0 +1,16 @@ +#![feature(more_qualified_paths)] + +struct Struct {} + +trait Trait { + type Type; +} + +impl Trait for Struct { + type Type = Self; +} + +fn main() { + // keep the qualified path details + let _ = ::Type {}; +} diff --git a/tests/target/match.rs b/tests/target/match.rs index e2c522bea10b1..1bf3fb758ee86 100644 --- a/tests/target/match.rs +++ b/tests/target/match.rs @@ -608,3 +608,22 @@ fn issue_3774() { } } } + +// #4109 +fn issue_4109() { + match () { + _ => { + #[cfg(debug_assertions)] + { + println!("Foo"); + } + } + } + + match () { + _ => { + #[allow(unsafe_code)] + unsafe {} + } + } +} diff --git a/tests/target/skip/preserve_trailing_comment.rs b/tests/target/skip/preserve_trailing_comment.rs new file mode 100644 index 0000000000000..f85de33257ccc --- /dev/null +++ b/tests/target/skip/preserve_trailing_comment.rs @@ -0,0 +1,7 @@ +#![rustfmt::skip] + +fn main() { + println!("Hello, world!"); +} + +// Trailing Comment From 737e6f704671dbf30e81dababa0133b585c03b48 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 19 Dec 2021 21:27:25 -0500 Subject: [PATCH 02/64] Improve out of line module resolution Fixes 5119 When the directory structure was laid out as follows: ``` dir |---mod_a | |---sub_mod_1.rs | |---sub_mod_2.rs |---mod_a.rs ``` And ``mod_a.rs`` contains the following content: ```rust mod mod_a { mod sub_mod_1; mod sub_mod_2; } ``` rustfmt previously tried to find ``sub_mod_1.rs`` and ``sub_mod_2.rs`` in ``./mod_a/mod_a/``. This directory does not exist and this caused rustfmt to fail with the error message: Error writing files: failed to resolve mod Now, both ``sub_mod_1.rs`` and ``sub_mod_2.rs`` are resolved correctly and found at ``mod_a/sub_mod_1.rs`` and ``mod_a/sub_mod_2.rs``. --- src/modules.rs | 9 ++++++- src/test/mod_resolver.rs | 14 +++++++++++ tests/cargo-fmt/main.rs | 24 +++++++++++++++++++ .../test-submodule-issue-5119/Cargo.toml | 8 +++++++ .../test-submodule-issue-5119/src/lib.rs | 7 ++++++ .../test-submodule-issue-5119/tests/test1.rs | 8 +++++++ .../tests/test1/sub1.rs | 6 +++++ .../tests/test1/sub2.rs | 6 +++++ .../tests/test1/sub3/mod.rs | 1 + .../tests/test1/sub3/sub4.rs | 0 10 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 tests/mod-resolver/test-submodule-issue-5119/Cargo.toml create mode 100644 tests/mod-resolver/test-submodule-issue-5119/src/lib.rs create mode 100644 tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs create mode 100644 tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs create mode 100644 tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs create mode 100644 tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs create mode 100644 tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs diff --git a/src/modules.rs b/src/modules.rs index 9c964b274e088..70b937b02836f 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -458,6 +458,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { self.directory.path.push(path.as_str()); self.directory.ownership = DirectoryOwnership::Owned { relative: None }; } else { + let id = id.as_str(); // We have to push on the current module name in the case of relative // paths in order to ensure that any additional module paths from inline // `mod x { ... }` come after the relative extension. @@ -468,9 +469,15 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { if let Some(ident) = relative.take() { // remove the relative offset self.directory.path.push(ident.as_str()); + + // In the case where there is an x.rs and an ./x directory we want + // to prevent adding x twice. For example, ./x/x + if self.directory.path.exists() && !self.directory.path.join(id).exists() { + return; + } } } - self.directory.path.push(id.as_str()); + self.directory.path.push(id); } } diff --git a/src/test/mod_resolver.rs b/src/test/mod_resolver.rs index ec9ed0f0b8d62..fcff6d14e6fa4 100644 --- a/src/test/mod_resolver.rs +++ b/src/test/mod_resolver.rs @@ -50,3 +50,17 @@ fn skip_out_of_line_nested_inline_within_out_of_line() { &["tests/mod-resolver/skip-files-issue-5065/one.rs"], ); } + +#[test] +fn fmt_out_of_line_test_modules() { + // See also https://github.com/rust-lang/rustfmt/issues/5119 + verify_mod_resolution( + "tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs", + &[ + "tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs", + "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs", + "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs", + "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs", + ], + ) +} diff --git a/tests/cargo-fmt/main.rs b/tests/cargo-fmt/main.rs index 5493b09e4aa63..bf81f253f6913 100644 --- a/tests/cargo-fmt/main.rs +++ b/tests/cargo-fmt/main.rs @@ -2,6 +2,7 @@ use std::env; use std::process::Command; +use std::path::Path; /// Run the cargo-fmt executable and return its output. fn cargo_fmt(args: &[&str]) -> (String, String) { @@ -71,3 +72,26 @@ fn rustfmt_help() { assert_that!(&["--", "-h"], contains("Format Rust code")); assert_that!(&["--", "--help=config"], contains("Configuration Options:")); } + +#[test] +fn cargo_fmt_out_of_line_test_modules() { + // See also https://github.com/rust-lang/rustfmt/issues/5119 + let expected_modified_files = [ + "tests/mod-resolver/test-submodule-issue-5119/src/lib.rs", + "tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs", + "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs", + "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs", + "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs", + ]; + let args = [ + "-v", + "--check", + "--manifest-path", + "tests/mod-resolver/test-submodule-issue-5119/Cargo.toml", + ]; + let (stdout, _) = cargo_fmt(&args); + for file in expected_modified_files { + let path = Path::new(file).canonicalize().unwrap(); + assert!(stdout.contains(&format!("Diff in {}", path.display()))) + } +} diff --git a/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml b/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml new file mode 100644 index 0000000000000..0993f12795991 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rustfmt-test-submodule-issue" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs b/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs new file mode 100644 index 0000000000000..3f7ddba8a288c --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs @@ -0,0 +1,7 @@ +pub fn foo() -> i32 { +3 +} + +pub fn bar() -> i32 { +4 +} diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs new file mode 100644 index 0000000000000..da4e86169ad92 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs @@ -0,0 +1,8 @@ +mod test1 { +#[cfg(unix)] +mod sub1; +#[cfg(not(unix))] +mod sub2; + +mod sub3; +} diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs new file mode 100644 index 0000000000000..b760ba23cd277 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs @@ -0,0 +1,6 @@ +use rustfmt_test_submodule_issue::foo; + +#[test] +fn test_foo() { +assert_eq!(3, foo()); +} diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs new file mode 100644 index 0000000000000..4fd8286eac400 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs @@ -0,0 +1,6 @@ +use rustfmt_test_submodule_issue::bar; + +#[test] +fn test_bar() { +assert_eq!(4, bar()); +} diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs new file mode 100644 index 0000000000000..e029785bc2457 --- /dev/null +++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs @@ -0,0 +1 @@ +mod sub4; diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs new file mode 100644 index 0000000000000..e69de29bb2d1d From 93b7de5b0110a1ab79714c3dfd009a4aae3e34b1 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Wed, 6 Nov 2019 10:15:34 +0000 Subject: [PATCH 03/64] Make `--check` work when running from stdin. (#3896) # Conflicts: # src/bin/main.rs --- src/bin/main.rs | 31 ++++++----- src/emitter/checkstyle.rs | 12 ++-- src/emitter/json.rs | 20 +++++-- src/test/mod.rs | 93 +++++++++++++++++++++++++++++++ tests/writemode/source/stdin.rs | 6 ++ tests/writemode/target/stdin.json | 1 + tests/writemode/target/stdin.xml | 2 + 7 files changed, 139 insertions(+), 26 deletions(-) create mode 100644 tests/writemode/source/stdin.rs create mode 100644 tests/writemode/target/stdin.json create mode 100644 tests/writemode/target/stdin.xml diff --git a/src/bin/main.rs b/src/bin/main.rs index 4d845547cdfed..6f5b09fc86adf 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -74,14 +74,10 @@ pub enum OperationError { /// An io error during reading or writing. #[error("{0}")] IoError(IoError), - /// Attempt to use --check with stdin, which isn't currently - /// supported. - #[error("The `--check` option is not supported with standard input.")] - CheckWithStdin, - /// Attempt to use --emit=json with stdin, which isn't currently - /// supported. - #[error("Using `--emit` other than stdout is not supported with standard input.")] - EmitWithStdin, + /// Attempt to use --emit with a mode which is not currently + /// supported with stdandard input. + #[error("Emit mode {0} not supported with standard output.")] + StdinBadEmit(EmitMode), } impl From for OperationError { @@ -255,15 +251,20 @@ fn format_string(input: String, options: GetOptsOptions) -> Result { let (mut config, _) = load_config(Some(Path::new(".")), Some(options.clone()))?; if options.check { - return Err(OperationError::CheckWithStdin.into()); - } - if let Some(emit_mode) = options.emit_mode { - if emit_mode != EmitMode::Stdout { - return Err(OperationError::EmitWithStdin.into()); + config.set().emit_mode(EmitMode::Diff); + } else { + match options.emit_mode { + // Emit modes which work with standard input + // None means default, which is Stdout. + None | Some(EmitMode::Stdout) | Some(EmitMode::Checkstyle) | Some(EmitMode::Json) => {} + Some(emit_mode) => { + return Err(OperationError::StdinBadEmit(emit_mode).into()); + } } + config + .set() + .emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout)); } - // emit mode is always Stdout for Stdin. - config.set().emit_mode(EmitMode::Stdout); config.set().verbose(Verbosity::Quiet); // parse file_lines diff --git a/src/emitter/checkstyle.rs b/src/emitter/checkstyle.rs index 76f2527db3dad..545b259979d91 100644 --- a/src/emitter/checkstyle.rs +++ b/src/emitter/checkstyle.rs @@ -2,7 +2,6 @@ use self::xml::XmlEscaped; use super::*; use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch}; use std::io::{self, Write}; -use std::path::Path; mod xml; @@ -30,7 +29,6 @@ impl Emitter for CheckstyleEmitter { }: FormattedFile<'_>, ) -> Result { const CONTEXT_SIZE: usize = 0; - let filename = ensure_real_path(filename); let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE); output_checkstyle_file(output, filename, diff)?; Ok(EmitterResult::default()) @@ -39,13 +37,13 @@ impl Emitter for CheckstyleEmitter { pub(crate) fn output_checkstyle_file( mut writer: T, - filename: &Path, + filename: &FileName, diff: Vec, ) -> Result<(), io::Error> where T: Write, { - write!(writer, r#""#, filename.display())?; + write!(writer, r#""#, filename)?; for mismatch in diff { let begin_line = mismatch.line_number; let mut current_line; @@ -77,7 +75,11 @@ mod tests { fn emits_empty_record_on_file_with_no_mismatches() { let file_name = "src/well_formatted.rs"; let mut writer = Vec::new(); - let _ = output_checkstyle_file(&mut writer, &PathBuf::from(file_name), vec![]); + let _ = output_checkstyle_file( + &mut writer, + &FileName::Real(PathBuf::from(file_name)), + vec![], + ); assert_eq!( &writer[..], format!(r#""#, file_name).as_bytes() diff --git a/src/emitter/json.rs b/src/emitter/json.rs index 269dd2d4daf57..4d6f972c5e3d8 100644 --- a/src/emitter/json.rs +++ b/src/emitter/json.rs @@ -3,7 +3,6 @@ use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch}; use serde::Serialize; use serde_json::to_string as to_json_string; use std::io::{self, Write}; -use std::path::Path; #[derive(Debug, Default)] pub(crate) struct JsonEmitter { @@ -47,7 +46,6 @@ impl Emitter for JsonEmitter { }: FormattedFile<'_>, ) -> Result { const CONTEXT_SIZE: usize = 0; - let filename = ensure_real_path(filename); let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE); let has_diff = !diff.is_empty(); @@ -62,7 +60,7 @@ impl Emitter for JsonEmitter { fn output_json_file( mut writer: T, - filename: &Path, + filename: &FileName, diff: Vec, num_emitted_files: u32, ) -> Result<(), io::Error> @@ -106,7 +104,7 @@ where }); } let json = to_json_string(&MismatchedFile { - name: String::from(filename.to_str().unwrap()), + name: format!("{}", filename), mismatches, })?; let prefix = if num_emitted_files > 0 { "," } else { "" }; @@ -148,7 +146,12 @@ mod tests { let mut writer = Vec::new(); let exp_json = to_json_string(&mismatched_file).unwrap(); - let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch], 0); + let _ = output_json_file( + &mut writer, + &FileName::Real(PathBuf::from(file)), + vec![mismatch], + 0, + ); assert_eq!(&writer[..], format!("{}", exp_json).as_bytes()); } @@ -188,7 +191,12 @@ mod tests { let mut writer = Vec::new(); let exp_json = to_json_string(&mismatched_file).unwrap(); - let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch], 0); + let _ = output_json_file( + &mut writer, + &FileName::Real(PathBuf::from(file)), + vec![mismatch], + 0, + ); assert_eq!(&writer[..], format!("{}", exp_json).as_bytes()); } diff --git a/src/test/mod.rs b/src/test/mod.rs index cceb28dfea6d7..e1a7972ec829c 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -307,6 +307,52 @@ fn assert_output(source: &Path, expected_filename: &Path) { } } +// Helper function for comparing the results of rustfmt +// to a known output generated by one of the write modes. +fn assert_stdin_output( + source: &Path, + expected_filename: &Path, + emit_mode: EmitMode, + has_diff: bool, +) { + let mut config = Config::default(); + config.set().newline_style(NewlineStyle::Unix); + config.set().emit_mode(emit_mode); + + let mut source_file = fs::File::open(&source).expect("couldn't open source"); + let mut source_text = String::new(); + source_file + .read_to_string(&mut source_text) + .expect("Failed reading target"); + let input = Input::Text(source_text); + + // Populate output by writing to a vec. + let mut buf: Vec = vec![]; + { + let mut session = Session::new(config, Some(&mut buf)); + session.format(input).unwrap(); + let errors = ReportedErrors { + has_diff: has_diff, + ..Default::default() + }; + assert_eq!(session.errors, errors); + } + + let mut expected_file = fs::File::open(&expected_filename).expect("couldn't open target"); + let mut expected_text = String::new(); + expected_file + .read_to_string(&mut expected_text) + .expect("Failed reading target"); + + let output = String::from_utf8(buf).unwrap(); + let compare = make_diff(&expected_text, &output, DIFF_CONTEXT_SIZE); + if !compare.is_empty() { + let mut failures = HashMap::new(); + failures.insert(source.to_owned(), compare); + print_mismatches_default_message(failures); + panic!("Text does not match expected output"); + } +} // Idempotence tests. Files in tests/target are checked to be unaltered by // rustfmt. #[nightly_only_test] @@ -463,6 +509,30 @@ fn stdin_works_with_modified_lines() { assert_eq!(buf, output.as_bytes()); } +/// Ensures that `EmitMode::Json` works with input from `stdin`. +#[test] +fn stdin_works_with_json() { + init_log(); + assert_stdin_output( + Path::new("tests/writemode/source/stdin.rs"), + Path::new("tests/writemode/target/stdin.json"), + EmitMode::Json, + true, + ); +} + +/// Ensures that `EmitMode::Checkstyle` works with input from `stdin`. +#[test] +fn stdin_works_with_checkstyle() { + init_log(); + assert_stdin_output( + Path::new("tests/writemode/source/stdin.rs"), + Path::new("tests/writemode/target/stdin.xml"), + EmitMode::Checkstyle, + false, + ); +} + #[test] fn stdin_disable_all_formatting_test() { init_log(); @@ -896,3 +966,26 @@ fn verify_check_works() { .status() .expect("run with check option failed"); } + +#[test] +fn verify_check_works_with_stdin() { + init_log(); + + let mut child = Command::new(rustfmt().to_str().unwrap()) + .arg("--check") + .stdin(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("run with check option failed"); + + { + let stdin = child.stdin.as_mut().expect("Failed to open stdin"); + stdin + .write_all("fn main() {}\n".as_bytes()) + .expect("Failed to write to rustfmt --check"); + } + let output = child + .wait_with_output() + .expect("Failed to wait on rustfmt child"); + assert!(output.status.success()); +} diff --git a/tests/writemode/source/stdin.rs b/tests/writemode/source/stdin.rs new file mode 100644 index 0000000000000..06f8a0c288d7f --- /dev/null +++ b/tests/writemode/source/stdin.rs @@ -0,0 +1,6 @@ + +fn + some( ) +{ +} +fn main () {} diff --git a/tests/writemode/target/stdin.json b/tests/writemode/target/stdin.json new file mode 100644 index 0000000000000..20e38f57f4a79 --- /dev/null +++ b/tests/writemode/target/stdin.json @@ -0,0 +1 @@ +[{"name":"stdin","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}","expected":"fn some() {}\nfn main() {}"}]}] \ No newline at end of file diff --git a/tests/writemode/target/stdin.xml b/tests/writemode/target/stdin.xml new file mode 100644 index 0000000000000..e70708338f563 --- /dev/null +++ b/tests/writemode/target/stdin.xml @@ -0,0 +1,2 @@ + + From 34263cd6bdb29df23258a96ea18fe986efe8dad0 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Tue, 12 Nov 2019 02:55:04 +0000 Subject: [PATCH 04/64] Fix --check -l with stdin. (#3910) * Fix some possible panics when using `--check` with stdin. One case which doesn't work is when there are only line ending fixes; with stdin rustfmt is unable to detect the difference as it stores the input with Unix line endings. * Add test for `rustfmt --check -l` with stdin. --- src/emitter/diff.rs | 5 ++--- src/test/mod.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/emitter/diff.rs b/src/emitter/diff.rs index 7264ad8bbf365..5e1f134465605 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, "{}", ensure_real_path(filename).display())?; + writeln!(output, "{}", filename)?; } else { print_diff( mismatch, @@ -40,8 +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. - let file_path = ensure_real_path(filename); - writeln!(output, "Incorrect newline style in {}", file_path.display())?; + writeln!(output, "Incorrect newline style in {}", filename)?; return Ok(EmitterResult { has_diff: true }); } diff --git a/src/test/mod.rs b/src/test/mod.rs index e1a7972ec829c..db1cf88479cf1 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -989,3 +989,29 @@ fn verify_check_works_with_stdin() { .expect("Failed to wait on rustfmt child"); assert!(output.status.success()); } + +#[test] +fn verify_check_l_works_with_stdin() { + init_log(); + + let mut child = Command::new(rustfmt().to_str().unwrap()) + .arg("--check") + .arg("-l") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("run with check option failed"); + + { + let stdin = child.stdin.as_mut().expect("Failed to open stdin"); + stdin + .write_all("fn main()\n{}\n".as_bytes()) + .expect("Failed to write to rustfmt --check"); + } + let output = child + .wait_with_output() + .expect("Failed to wait on rustfmt child"); + assert!(output.status.success()); + assert_eq!(std::str::from_utf8(&output.stdout).unwrap(), "stdin\n"); +} From 776baf93f8fd1e2f3a66da4c2a1dd92114cef9c6 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 9 Dec 2019 04:41:16 -0600 Subject: [PATCH 05/64] refactor: update json emitter to better handle errors (#3953) --- src/emitter/json.rs | 169 +++++++++++++---------------- tests/writemode/target/output.json | 2 +- tests/writemode/target/stdin.json | 2 +- 3 files changed, 80 insertions(+), 93 deletions(-) diff --git a/src/emitter/json.rs b/src/emitter/json.rs index 4d6f972c5e3d8..7c0f862cbc63f 100644 --- a/src/emitter/json.rs +++ b/src/emitter/json.rs @@ -6,10 +6,10 @@ use std::io::{self, Write}; #[derive(Debug, Default)] pub(crate) struct JsonEmitter { - num_files: u32, + mismatched_files: Vec, } -#[derive(Debug, Default, Serialize)] +#[derive(Debug, Default, PartialEq, Serialize)] struct MismatchedBlock { original_begin_line: u32, original_end_line: u32, @@ -19,26 +19,20 @@ struct MismatchedBlock { expected: String, } -#[derive(Debug, Default, Serialize)] +#[derive(Debug, Default, PartialEq, Serialize)] struct MismatchedFile { name: String, mismatches: Vec, } impl Emitter for JsonEmitter { - fn emit_header(&self, output: &mut dyn Write) -> Result<(), io::Error> { - write!(output, "[")?; - Ok(()) - } - fn emit_footer(&self, output: &mut dyn Write) -> Result<(), io::Error> { - write!(output, "]")?; - Ok(()) + writeln!(output, "{}", &to_json_string(&self.mismatched_files)?) } fn emit_formatted_file( &mut self, - output: &mut dyn Write, + _output: &mut dyn Write, FormattedFile { filename, original_text, @@ -50,66 +44,61 @@ impl Emitter for JsonEmitter { let has_diff = !diff.is_empty(); if has_diff { - output_json_file(output, filename, diff, self.num_files)?; - self.num_files += 1; + self.add_misformatted_file(filename, diff)?; } Ok(EmitterResult { has_diff }) } } -fn output_json_file( - mut writer: T, - filename: &FileName, - diff: Vec, - num_emitted_files: u32, -) -> Result<(), io::Error> -where - T: Write, -{ - let mut mismatches = vec![]; - for mismatch in diff { - let original_begin_line = mismatch.line_number_orig; - let expected_begin_line = mismatch.line_number; - let mut original_end_line = original_begin_line; - let mut expected_end_line = expected_begin_line; - let mut original_line_counter = 0; - let mut expected_line_counter = 0; - let mut original_lines = vec![]; - let mut expected_lines = vec![]; +impl JsonEmitter { + fn add_misformatted_file( + &mut self, + filename: &FileName, + diff: Vec, + ) -> Result<(), io::Error> { + let mut mismatches = vec![]; + for mismatch in diff { + let original_begin_line = mismatch.line_number_orig; + let expected_begin_line = mismatch.line_number; + let mut original_end_line = original_begin_line; + let mut expected_end_line = expected_begin_line; + let mut original_line_counter = 0; + let mut expected_line_counter = 0; + let mut original_lines = vec![]; + let mut expected_lines = vec![]; - for line in mismatch.lines { - match line { - DiffLine::Expected(msg) => { - expected_end_line = expected_begin_line + expected_line_counter; - expected_line_counter += 1; - expected_lines.push(msg) - } - DiffLine::Resulting(msg) => { - original_end_line = original_begin_line + original_line_counter; - original_line_counter += 1; - original_lines.push(msg) + for line in mismatch.lines { + match line { + DiffLine::Expected(msg) => { + expected_end_line = expected_begin_line + expected_line_counter; + expected_line_counter += 1; + expected_lines.push(msg) + } + DiffLine::Resulting(msg) => { + original_end_line = original_begin_line + original_line_counter; + original_line_counter += 1; + original_lines.push(msg) + } + DiffLine::Context(_) => continue, } - DiffLine::Context(_) => continue, } - } - mismatches.push(MismatchedBlock { - original_begin_line, - original_end_line, - expected_begin_line, - expected_end_line, - original: original_lines.join("\n"), - expected: expected_lines.join("\n"), + mismatches.push(MismatchedBlock { + original_begin_line, + original_end_line, + expected_begin_line, + expected_end_line, + original: original_lines.join("\n"), + expected: expected_lines.join("\n"), + }); + } + self.mismatched_files.push(MismatchedFile { + name: format!("{}", filename), + mismatches, }); + Ok(()) } - let json = to_json_string(&MismatchedFile { - name: format!("{}", filename), - mismatches, - })?; - let prefix = if num_emitted_files > 0 { "," } else { "" }; - write!(writer, "{}{}", prefix, &json)?; - Ok(()) } #[cfg(test)] @@ -120,6 +109,9 @@ mod tests { #[test] fn expected_line_range_correct_when_single_line_split() { + let mut emitter = JsonEmitter { + mismatched_files: vec![], + }; let file = "foo/bar.rs"; let mismatched_file = MismatchedFile { name: String::from(file), @@ -144,19 +136,19 @@ mod tests { ], }; - let mut writer = Vec::new(); - let exp_json = to_json_string(&mismatched_file).unwrap(); - let _ = output_json_file( - &mut writer, - &FileName::Real(PathBuf::from(file)), - vec![mismatch], - 0, - ); - assert_eq!(&writer[..], format!("{}", exp_json).as_bytes()); + let _ = emitter + .add_misformatted_file(&FileName::Real(PathBuf::from(file)), vec![mismatch]) + .unwrap(); + + assert_eq!(emitter.mismatched_files.len(), 1); + assert_eq!(emitter.mismatched_files[0], mismatched_file); } #[test] fn context_lines_ignored() { + let mut emitter = JsonEmitter { + mismatched_files: vec![], + }; let file = "src/lib.rs"; let mismatched_file = MismatchedFile { name: String::from(file), @@ -189,15 +181,12 @@ mod tests { ], }; - let mut writer = Vec::new(); - let exp_json = to_json_string(&mismatched_file).unwrap(); - let _ = output_json_file( - &mut writer, - &FileName::Real(PathBuf::from(file)), - vec![mismatch], - 0, - ); - assert_eq!(&writer[..], format!("{}", exp_json).as_bytes()); + let _ = emitter + .add_misformatted_file(&FileName::Real(PathBuf::from(file)), vec![mismatch]) + .unwrap(); + + assert_eq!(emitter.mismatched_files.len(), 1); + assert_eq!(emitter.mismatched_files[0], mismatched_file); } #[test] @@ -217,7 +206,7 @@ mod tests { .unwrap(); let _ = emitter.emit_footer(&mut writer); assert_eq!(result.has_diff, false); - assert_eq!(&writer[..], "[]".as_bytes()); + assert_eq!(&writer[..], "[]\n".as_bytes()); } #[test] @@ -263,7 +252,7 @@ mod tests { ) .unwrap(); let _ = emitter.emit_footer(&mut writer); - let exp_json = to_json_string(&MismatchedFile { + let exp_json = to_json_string(&vec![MismatchedFile { name: String::from(file_name), mismatches: vec![ MismatchedBlock { @@ -287,10 +276,10 @@ mod tests { ), }, ], - }) + }]) .unwrap(); assert_eq!(result.has_diff, true); - assert_eq!(&writer[..], format!("[{}]", exp_json).as_bytes()); + assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes()); } #[test] @@ -325,7 +314,7 @@ mod tests { ) .unwrap(); let _ = emitter.emit_footer(&mut writer); - let exp_bin_json = to_json_string(&MismatchedFile { + let exp_bin = MismatchedFile { name: String::from(bin_file), mismatches: vec![MismatchedBlock { original_begin_line: 2, @@ -335,9 +324,9 @@ mod tests { original: String::from("println!(\"Hello, world!\");"), expected: String::from(" println!(\"Hello, world!\");"), }], - }) - .unwrap(); - let exp_lib_json = to_json_string(&MismatchedFile { + }; + + let exp_lib = MismatchedFile { name: String::from(lib_file), mismatches: vec![MismatchedBlock { original_begin_line: 2, @@ -347,11 +336,9 @@ mod tests { original: String::from("println!(\"Greetings!\");"), expected: String::from(" println!(\"Greetings!\");"), }], - }) - .unwrap(); - assert_eq!( - &writer[..], - format!("[{},{}]", exp_bin_json, exp_lib_json).as_bytes() - ); + }; + + let exp_json = to_json_string(&vec![exp_bin, exp_lib]).unwrap(); + assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes()); } } diff --git a/tests/writemode/target/output.json b/tests/writemode/target/output.json index b5f327b0a1ca6..acb33dea7ef39 100644 --- a/tests/writemode/target/output.json +++ b/tests/writemode/target/output.json @@ -1 +1 @@ -[{"name":"tests/writemode/source/json.rs","mismatches":[{"original_begin_line":5,"original_end_line":7,"expected_begin_line":5,"expected_end_line":5,"original":"fn foo_expr() {\n 1\n}","expected":"fn foo_expr() { 1 }"},{"original_begin_line":9,"original_end_line":11,"expected_begin_line":7,"expected_end_line":7,"original":"fn foo_stmt() {\n foo();\n}","expected":"fn foo_stmt() { foo(); }"},{"original_begin_line":13,"original_end_line":15,"expected_begin_line":9,"expected_end_line":9,"original":"fn foo_decl_local() {\n let z = 5;\n }","expected":"fn foo_decl_local() { let z = 5; }"},{"original_begin_line":17,"original_end_line":19,"expected_begin_line":11,"expected_end_line":11,"original":"fn foo_decl_item(x: &mut i32) {\n x = 3;\n}","expected":"fn foo_decl_item(x: &mut i32) { x = 3; }"},{"original_begin_line":21,"original_end_line":21,"expected_begin_line":13,"expected_end_line":13,"original":" fn empty() {","expected":"fn empty() {}"},{"original_begin_line":23,"original_end_line":23,"expected_begin_line":15,"expected_end_line":15,"original":"}","expected":"fn foo_return() -> String { \"yay\" }"},{"original_begin_line":25,"original_end_line":29,"expected_begin_line":17,"expected_end_line":20,"original":"fn foo_return() -> String {\n \"yay\"\n}\n\nfn foo_where() -> T where T: Sync {","expected":"fn foo_where() -> T\nwhere\n T: Sync,\n{"},{"original_begin_line":64,"original_end_line":66,"expected_begin_line":55,"expected_end_line":55,"original":"fn lots_of_space () {\n 1 \n}","expected":"fn lots_of_space() { 1 }"},{"original_begin_line":71,"original_end_line":72,"expected_begin_line":60,"expected_end_line":60,"original":" fn dummy(&self) {\n }","expected":" fn dummy(&self) {}"},{"original_begin_line":75,"original_end_line":75,"expected_begin_line":63,"expected_end_line":64,"original":"trait CoolerTypes { fn dummy(&self) { ","expected":"trait CoolerTypes {\n fn dummy(&self) {}"},{"original_begin_line":77,"original_end_line":77,"expected_begin_line":66,"expected_end_line":66,"original":"}","expected":""},{"original_begin_line":79,"original_end_line":79,"expected_begin_line":67,"expected_end_line":70,"original":"fn Foo() where T: Bar {","expected":"fn Foo()\nwhere\n T: Bar,\n{"}]}] \ No newline at end of file +[{"name":"tests/writemode/source/json.rs","mismatches":[{"original_begin_line":5,"original_end_line":7,"expected_begin_line":5,"expected_end_line":5,"original":"fn foo_expr() {\n 1\n}","expected":"fn foo_expr() { 1 }"},{"original_begin_line":9,"original_end_line":11,"expected_begin_line":7,"expected_end_line":7,"original":"fn foo_stmt() {\n foo();\n}","expected":"fn foo_stmt() { foo(); }"},{"original_begin_line":13,"original_end_line":15,"expected_begin_line":9,"expected_end_line":9,"original":"fn foo_decl_local() {\n let z = 5;\n }","expected":"fn foo_decl_local() { let z = 5; }"},{"original_begin_line":17,"original_end_line":19,"expected_begin_line":11,"expected_end_line":11,"original":"fn foo_decl_item(x: &mut i32) {\n x = 3;\n}","expected":"fn foo_decl_item(x: &mut i32) { x = 3; }"},{"original_begin_line":21,"original_end_line":21,"expected_begin_line":13,"expected_end_line":13,"original":" fn empty() {","expected":"fn empty() {}"},{"original_begin_line":23,"original_end_line":23,"expected_begin_line":15,"expected_end_line":15,"original":"}","expected":"fn foo_return() -> String { \"yay\" }"},{"original_begin_line":25,"original_end_line":29,"expected_begin_line":17,"expected_end_line":20,"original":"fn foo_return() -> String {\n \"yay\"\n}\n\nfn foo_where() -> T where T: Sync {","expected":"fn foo_where() -> T\nwhere\n T: Sync,\n{"},{"original_begin_line":64,"original_end_line":66,"expected_begin_line":55,"expected_end_line":55,"original":"fn lots_of_space () {\n 1 \n}","expected":"fn lots_of_space() { 1 }"},{"original_begin_line":71,"original_end_line":72,"expected_begin_line":60,"expected_end_line":60,"original":" fn dummy(&self) {\n }","expected":" fn dummy(&self) {}"},{"original_begin_line":75,"original_end_line":75,"expected_begin_line":63,"expected_end_line":64,"original":"trait CoolerTypes { fn dummy(&self) { ","expected":"trait CoolerTypes {\n fn dummy(&self) {}"},{"original_begin_line":77,"original_end_line":77,"expected_begin_line":66,"expected_end_line":66,"original":"}","expected":""},{"original_begin_line":79,"original_end_line":79,"expected_begin_line":67,"expected_end_line":70,"original":"fn Foo() where T: Bar {","expected":"fn Foo()\nwhere\n T: Bar,\n{"}]}] diff --git a/tests/writemode/target/stdin.json b/tests/writemode/target/stdin.json index 20e38f57f4a79..ae6796863e5e9 100644 --- a/tests/writemode/target/stdin.json +++ b/tests/writemode/target/stdin.json @@ -1 +1 @@ -[{"name":"stdin","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}","expected":"fn some() {}\nfn main() {}"}]}] \ No newline at end of file +[{"name":"stdin","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}","expected":"fn some() {}\nfn main() {}"}]}] From 894a3c0e7745ece39fcee89d38389a789cfe520a Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 23 Jun 2020 01:20:29 +0100 Subject: [PATCH 06/64] Fix newlines in JSON output (#4262) * Fix newlines in JSON output This changes the JSON output to be more consistent about where newlines are included. Previously it only included them between lines in a multiline diff. That meant single line changes were treated a bit weirdly. This changes it to append a newline to every line. When feeding the results into `arc lint` this behaves correctly. I have only done limited testing though, in particular there's a possibility it might not work with files with `\r\n` endings (though that would have been the case before too). Fixes #4259 * Update tests # Conflicts: # tests/writemode/target/output.json --- src/emitter/json.rs | 38 ++++++++++++++++-------------- tests/writemode/target/output.json | 2 +- tests/writemode/target/stdin.json | 2 +- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/emitter/json.rs b/src/emitter/json.rs index 7c0f862cbc63f..c7f68d4675a67 100644 --- a/src/emitter/json.rs +++ b/src/emitter/json.rs @@ -65,20 +65,22 @@ impl JsonEmitter { let mut expected_end_line = expected_begin_line; let mut original_line_counter = 0; let mut expected_line_counter = 0; - let mut original_lines = vec![]; - let mut expected_lines = vec![]; + let mut original = String::new(); + let mut expected = String::new(); for line in mismatch.lines { match line { DiffLine::Expected(msg) => { expected_end_line = expected_begin_line + expected_line_counter; expected_line_counter += 1; - expected_lines.push(msg) + expected.push_str(&msg); + expected.push('\n'); } DiffLine::Resulting(msg) => { original_end_line = original_begin_line + original_line_counter; original_line_counter += 1; - original_lines.push(msg) + original.push_str(&msg); + original.push('\n'); } DiffLine::Context(_) => continue, } @@ -89,8 +91,8 @@ impl JsonEmitter { original_end_line, expected_begin_line, expected_end_line, - original: original_lines.join("\n"), - expected: expected_lines.join("\n"), + original, + expected, }); } self.mismatched_files.push(MismatchedFile { @@ -120,8 +122,8 @@ mod tests { original_end_line: 79, expected_begin_line: 79, expected_end_line: 82, - original: String::from("fn Foo() where T: Bar {"), - expected: String::from("fn Foo()\nwhere\n T: Bar,\n{"), + original: String::from("fn Foo() where T: Bar {\n"), + expected: String::from("fn Foo()\nwhere\n T: Bar,\n{\n"), }], }; let mismatch = Mismatch { @@ -158,10 +160,10 @@ mod tests { expected_begin_line: 5, expected_end_line: 5, original: String::from( - "fn foo(_x: &u64) -> Option<&(dyn::std::error::Error + 'static)> {", + "fn foo(_x: &u64) -> Option<&(dyn::std::error::Error + 'static)> {\n", ), expected: String::from( - "fn foo(_x: &u64) -> Option<&(dyn ::std::error::Error + 'static)> {", + "fn foo(_x: &u64) -> Option<&(dyn ::std::error::Error + 'static)> {\n", ), }], }; @@ -260,8 +262,8 @@ mod tests { original_end_line: 2, expected_begin_line: 2, expected_end_line: 2, - original: String::from("println!(\"Hello, world!\");"), - expected: String::from(" println!(\"Hello, world!\");"), + original: String::from("println!(\"Hello, world!\");\n"), + expected: String::from(" println!(\"Hello, world!\");\n"), }, MismatchedBlock { original_begin_line: 7, @@ -269,10 +271,10 @@ mod tests { expected_begin_line: 7, expected_end_line: 10, original: String::from( - "#[test]\nfn it_works() {\n assert_eq!(2 + 2, 4);\n}", + "#[test]\nfn it_works() {\n assert_eq!(2 + 2, 4);\n}\n", ), expected: String::from( - " #[test]\n fn it_works() {\n assert_eq!(2 + 2, 4);\n }", + " #[test]\n fn it_works() {\n assert_eq!(2 + 2, 4);\n }\n", ), }, ], @@ -321,8 +323,8 @@ mod tests { original_end_line: 2, expected_begin_line: 2, expected_end_line: 2, - original: String::from("println!(\"Hello, world!\");"), - expected: String::from(" println!(\"Hello, world!\");"), + original: String::from("println!(\"Hello, world!\");\n"), + expected: String::from(" println!(\"Hello, world!\");\n"), }], }; @@ -333,8 +335,8 @@ mod tests { original_end_line: 2, expected_begin_line: 2, expected_end_line: 2, - original: String::from("println!(\"Greetings!\");"), - expected: String::from(" println!(\"Greetings!\");"), + original: String::from("println!(\"Greetings!\");\n"), + expected: String::from(" println!(\"Greetings!\");\n"), }], }; diff --git a/tests/writemode/target/output.json b/tests/writemode/target/output.json index acb33dea7ef39..d8b5467ee91ca 100644 --- a/tests/writemode/target/output.json +++ b/tests/writemode/target/output.json @@ -1 +1 @@ -[{"name":"tests/writemode/source/json.rs","mismatches":[{"original_begin_line":5,"original_end_line":7,"expected_begin_line":5,"expected_end_line":5,"original":"fn foo_expr() {\n 1\n}","expected":"fn foo_expr() { 1 }"},{"original_begin_line":9,"original_end_line":11,"expected_begin_line":7,"expected_end_line":7,"original":"fn foo_stmt() {\n foo();\n}","expected":"fn foo_stmt() { foo(); }"},{"original_begin_line":13,"original_end_line":15,"expected_begin_line":9,"expected_end_line":9,"original":"fn foo_decl_local() {\n let z = 5;\n }","expected":"fn foo_decl_local() { let z = 5; }"},{"original_begin_line":17,"original_end_line":19,"expected_begin_line":11,"expected_end_line":11,"original":"fn foo_decl_item(x: &mut i32) {\n x = 3;\n}","expected":"fn foo_decl_item(x: &mut i32) { x = 3; }"},{"original_begin_line":21,"original_end_line":21,"expected_begin_line":13,"expected_end_line":13,"original":" fn empty() {","expected":"fn empty() {}"},{"original_begin_line":23,"original_end_line":23,"expected_begin_line":15,"expected_end_line":15,"original":"}","expected":"fn foo_return() -> String { \"yay\" }"},{"original_begin_line":25,"original_end_line":29,"expected_begin_line":17,"expected_end_line":20,"original":"fn foo_return() -> String {\n \"yay\"\n}\n\nfn foo_where() -> T where T: Sync {","expected":"fn foo_where() -> T\nwhere\n T: Sync,\n{"},{"original_begin_line":64,"original_end_line":66,"expected_begin_line":55,"expected_end_line":55,"original":"fn lots_of_space () {\n 1 \n}","expected":"fn lots_of_space() { 1 }"},{"original_begin_line":71,"original_end_line":72,"expected_begin_line":60,"expected_end_line":60,"original":" fn dummy(&self) {\n }","expected":" fn dummy(&self) {}"},{"original_begin_line":75,"original_end_line":75,"expected_begin_line":63,"expected_end_line":64,"original":"trait CoolerTypes { fn dummy(&self) { ","expected":"trait CoolerTypes {\n fn dummy(&self) {}"},{"original_begin_line":77,"original_end_line":77,"expected_begin_line":66,"expected_end_line":66,"original":"}","expected":""},{"original_begin_line":79,"original_end_line":79,"expected_begin_line":67,"expected_end_line":70,"original":"fn Foo() where T: Bar {","expected":"fn Foo()\nwhere\n T: Bar,\n{"}]}] +[{"name":"tests/writemode/source/json.rs","mismatches":[{"original_begin_line":5,"original_end_line":7,"expected_begin_line":5,"expected_end_line":5,"original":"fn foo_expr() {\n 1\n}\n","expected":"fn foo_expr() { 1 }\n"},{"original_begin_line":9,"original_end_line":11,"expected_begin_line":7,"expected_end_line":7,"original":"fn foo_stmt() {\n foo();\n}\n","expected":"fn foo_stmt() { foo(); }\n"},{"original_begin_line":13,"original_end_line":15,"expected_begin_line":9,"expected_end_line":9,"original":"fn foo_decl_local() {\n let z = 5;\n }\n","expected":"fn foo_decl_local() { let z = 5; }\n"},{"original_begin_line":17,"original_end_line":19,"expected_begin_line":11,"expected_end_line":11,"original":"fn foo_decl_item(x: &mut i32) {\n x = 3;\n}\n","expected":"fn foo_decl_item(x: &mut i32) { x = 3; }\n"},{"original_begin_line":21,"original_end_line":21,"expected_begin_line":13,"expected_end_line":13,"original":" fn empty() {\n","expected":"fn empty() {}\n"},{"original_begin_line":23,"original_end_line":23,"expected_begin_line":15,"expected_end_line":15,"original":"}\n","expected":"fn foo_return() -> String { \"yay\" }\n"},{"original_begin_line":25,"original_end_line":29,"expected_begin_line":17,"expected_end_line":20,"original":"fn foo_return() -> String {\n \"yay\"\n}\n\nfn foo_where() -> T where T: Sync {\n","expected":"fn foo_where() -> T\nwhere\n T: Sync,\n{\n"},{"original_begin_line":64,"original_end_line":66,"expected_begin_line":55,"expected_end_line":55,"original":"fn lots_of_space () {\n 1 \n}\n","expected":"fn lots_of_space() { 1 }\n"},{"original_begin_line":71,"original_end_line":72,"expected_begin_line":60,"expected_end_line":60,"original":" fn dummy(&self) {\n }\n","expected":" fn dummy(&self) {}\n"},{"original_begin_line":75,"original_end_line":75,"expected_begin_line":63,"expected_end_line":64,"original":"trait CoolerTypes { fn dummy(&self) { \n","expected":"trait CoolerTypes {\n fn dummy(&self) {}\n"},{"original_begin_line":77,"original_end_line":77,"expected_begin_line":66,"expected_end_line":66,"original":"}\n","expected":""},{"original_begin_line":79,"original_end_line":79,"expected_begin_line":67,"expected_end_line":70,"original":"fn Foo() where T: Bar {\n","expected":"fn Foo()\nwhere\n T: Bar,\n{\n"}]}] diff --git a/tests/writemode/target/stdin.json b/tests/writemode/target/stdin.json index ae6796863e5e9..6f5d5bfb8ca2a 100644 --- a/tests/writemode/target/stdin.json +++ b/tests/writemode/target/stdin.json @@ -1 +1 @@ -[{"name":"stdin","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}","expected":"fn some() {}\nfn main() {}"}]}] +[{"name":"stdin","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}\n","expected":"fn some() {}\nfn main() {}\n"}]}] From 34d374ee5d23951d166ca3f51d477ddf6526a2fa Mon Sep 17 00:00:00 2001 From: Seiichi Uchida Date: Fri, 3 Jul 2020 11:13:16 +0900 Subject: [PATCH 07/64] Use when emitting stdin as filename (#4298) # Conflicts: # src/config/file_lines.rs # src/rustfmt/main.rs # src/test/mod.rs --- src/config/file_lines.rs | 2 +- src/test/mod.rs | 6 +++--- tests/writemode/target/stdin.json | 2 +- tests/writemode/target/stdin.xml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/config/file_lines.rs b/src/config/file_lines.rs index 7b498dc46b320..e4e51a3f3b409 100644 --- a/src/config/file_lines.rs +++ b/src/config/file_lines.rs @@ -39,7 +39,7 @@ impl fmt::Display for FileName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { FileName::Real(p) => write!(f, "{}", p.to_str().unwrap()), - FileName::Stdin => write!(f, "stdin"), + FileName::Stdin => write!(f, ""), } } } diff --git a/src/test/mod.rs b/src/test/mod.rs index db1cf88479cf1..2d5a8f22053fa 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -466,9 +466,9 @@ fn stdin_formatting_smoke_test() { } #[cfg(not(windows))] - assert_eq!(buf, "stdin:\n\nfn main() {}\n".as_bytes()); + assert_eq!(buf, ":\n\nfn main() {}\n".as_bytes()); #[cfg(windows)] - assert_eq!(buf, "stdin:\n\nfn main() {}\r\n".as_bytes()); + assert_eq!(buf, ":\n\nfn main() {}\r\n".as_bytes()); } #[test] @@ -1013,5 +1013,5 @@ fn verify_check_l_works_with_stdin() { .wait_with_output() .expect("Failed to wait on rustfmt child"); assert!(output.status.success()); - assert_eq!(std::str::from_utf8(&output.stdout).unwrap(), "stdin\n"); + assert_eq!(std::str::from_utf8(&output.stdout).unwrap(), "\n"); } diff --git a/tests/writemode/target/stdin.json b/tests/writemode/target/stdin.json index 6f5d5bfb8ca2a..dbf2c48632295 100644 --- a/tests/writemode/target/stdin.json +++ b/tests/writemode/target/stdin.json @@ -1 +1 @@ -[{"name":"stdin","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}\n","expected":"fn some() {}\nfn main() {}\n"}]}] +[{"name":"","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}\n","expected":"fn some() {}\nfn main() {}\n"}]}] diff --git a/tests/writemode/target/stdin.xml b/tests/writemode/target/stdin.xml index e70708338f563..a7301bbc553c3 100644 --- a/tests/writemode/target/stdin.xml +++ b/tests/writemode/target/stdin.xml @@ -1,2 +1,2 @@ - - + + From 5056f4cfb311a084420f1828cd58af94d143f5e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Wed, 5 Jan 2022 21:19:36 +0100 Subject: [PATCH 08/64] some minor clippy fixes --- src/cargo-fmt/main.rs | 3 +-- src/format_report_formatter.rs | 1 + src/macros.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index 759b21218c353..8cb7b4585ecb2 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -387,8 +387,7 @@ fn get_targets_root_only( .unwrap_or_default() == current_dir_manifest }) - .map(|p| p.targets) - .flatten() + .flat_map(|p| p.targets) .collect(), }; diff --git a/src/format_report_formatter.rs b/src/format_report_formatter.rs index c820259256c47..90406cdb95e2b 100644 --- a/src/format_report_formatter.rs +++ b/src/format_report_formatter.rs @@ -20,6 +20,7 @@ impl<'a> FormatReportFormatterBuilder<'a> { } /// Enables colors and formatting in the output. + #[must_use] pub fn enable_colors(self, enable_colors: bool) -> Self { Self { enable_colors, diff --git a/src/macros.rs b/src/macros.rs index f29552caf8d87..fdbe3374615cd 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -152,7 +152,7 @@ pub(crate) fn rewrite_macro( ) -> Option { let should_skip = context .skip_context - .skip_macro(&context.snippet(mac.path.span).to_owned()); + .skip_macro(context.snippet(mac.path.span)); if should_skip { None } else { From 6e6300207f835c0a6c4dd0b6a4e55ab07d3d272b Mon Sep 17 00:00:00 2001 From: Lamb Date: Mon, 26 Jul 2021 05:38:16 +0200 Subject: [PATCH 09/64] Compute most of Public/Exported access level in rustc_resolve Mak DefId to AccessLevel map in resolve for export hir_id to accesslevel in resolve and applied in privacy using local def id removing tracing probes making function not recursive and adding comments Move most of Exported/Public res to rustc_resolve moving public/export res to resolve fix missing stability attributes in core, std and alloc move code to access_levels.rs return for some kinds instead of going through them Export correctness, macro changes, comments add comment for import binding add comment for import binding renmae to access level visitor, remove comments, move fn as closure, remove new_key fmt fix rebase fix rebase fmt fmt fix: move macro def to rustc_resolve fix: reachable AccessLevel for enum variants fmt fix: missing stability attributes for other architectures allow unreachable pub in rustfmt fix: missing impl access level + renaming export to reexport Missing impl access level was found thanks to a test in clippy --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index ad23b16e02ec1..fae8080c02e41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ #![warn(unreachable_pub)] #![recursion_limit = "256"] #![allow(clippy::match_like_matches_macro)] +#![allow(unreachable_pub)] #[macro_use] extern crate derive_new; From 04670a14046b96a17bee2c2b36c3c2002ce3b0b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 12 Jan 2022 00:00:00 +0000 Subject: [PATCH 10/64] Remove LLVM-style inline assembly from rustfmt --- src/expr.rs | 4 +--- src/utils.rs | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index c9c8852cd3b56..e1865c8afc2f0 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -334,9 +334,7 @@ pub(crate) fn format_expr( // satisfy our width restrictions. // Style Guide RFC for InlineAsm variant pending // https://github.com/rust-dev-tools/fmt-rfcs/issues/152 - ast::ExprKind::LlvmInlineAsm(..) | ast::ExprKind::InlineAsm(..) => { - Some(context.snippet(expr.span).to_owned()) - } + ast::ExprKind::InlineAsm(..) => Some(context.snippet(expr.span).to_owned()), ast::ExprKind::TryBlock(ref block) => { if let rw @ Some(_) = rewrite_single_line_block(context, "try ", block, Some(&expr.attrs), None, shape) diff --git a/src/utils.rs b/src/utils.rs index 0c0b789a6efd1..2428d8cb0fd89 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -507,7 +507,6 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr | ast::ExprKind::Err | ast::ExprKind::Field(..) | ast::ExprKind::InlineAsm(..) - | ast::ExprKind::LlvmInlineAsm(..) | ast::ExprKind::Let(..) | ast::ExprKind::Path(..) | ast::ExprKind::Range(..) From bfbf42cecbed85406b3b83482466da39ab5b4551 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Fri, 14 Jan 2022 18:18:37 -0600 Subject: [PATCH 11/64] fix(rustfmt): resolve generated file formatting issue --- Configurations.md | 4 ++-- src/config/mod.rs | 4 ++-- src/formatting.rs | 4 +++- src/test/mod.rs | 18 ++++++++++++++++++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Configurations.md b/Configurations.md index a89fbe863e652..4476f2a449b1f 100644 --- a/Configurations.md +++ b/Configurations.md @@ -929,9 +929,9 @@ fn add_one(x: i32) -> i32 { ## `format_generated_files` Format generated files. A file is considered generated -if any of the first five lines contains `@generated` marker. +if any of the first five lines contain a `@generated` comment marker. -- **Default value**: `false` +- **Default value**: `true` - **Possible values**: `true`, `false` - **Stable**: No (tracking issue: [#5080](https://github.com/rust-lang/rustfmt/issues/5080)) diff --git a/src/config/mod.rs b/src/config/mod.rs index 5dbe532ac388f..cd90e0904b6cd 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -138,7 +138,7 @@ create_config! { inline_attribute_width: usize, 0, false, "Write an item and its attribute on the same line \ if their combined width is below a threshold"; - format_generated_files: bool, false, false, "Format generated files"; + format_generated_files: bool, true, false, "Format generated files"; // Options that can change the source code beyond whitespace/blocks (somewhat linty things) merge_derives: bool, true, true, "Merge multiple `#[derive(...)]` into a single one"; @@ -606,7 +606,7 @@ blank_lines_lower_bound = 0 edition = "2015" version = "One" inline_attribute_width = 0 -format_generated_files = false +format_generated_files = true merge_derives = true use_try_shorthand = false use_field_init_shorthand = false diff --git a/src/formatting.rs b/src/formatting.rs index 67cf1232f66ab..ca93955a549dd 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -80,7 +80,9 @@ fn should_skip_module( return true; } - if !config.format_generated_files() { + // FIXME(calebcartwright) - we need to determine how we'll handle the + // `format_generated_files` option with stdin based input. + if !input_is_stdin && !config.format_generated_files() { let source_file = context.parse_session.span_to_file_contents(module.span); let src = source_file.src.as_ref().expect("SourceFile without src"); diff --git a/src/test/mod.rs b/src/test/mod.rs index cceb28dfea6d7..c50d18644b091 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -487,6 +487,24 @@ fn stdin_disable_all_formatting_test() { assert_eq!(input, String::from_utf8(output.stdout).unwrap()); } +#[test] +fn stdin_generated_files_issue_5172() { + init_log(); + let input = Input::Text("//@generated\nfn main() {}".to_owned()); + let mut config = Config::default(); + config.set().emit_mode(EmitMode::Stdout); + config.set().format_generated_files(false); + config.set().newline_style(NewlineStyle::Unix); + let mut buf: Vec = vec![]; + { + let mut session = Session::new(config, Some(&mut buf)); + session.format(input).unwrap(); + assert!(session.has_no_errors()); + } + // N.B. this should be changed once `format_generated_files` is supported with stdin + assert_eq!(buf, "stdin:\n\n//@generated\nfn main() {}\n".as_bytes()); +} + #[test] fn format_lines_errors_are_reported() { init_log(); From f5ce84e4f25304dfbf9efeb26a85ef3c9ad594ad Mon Sep 17 00:00:00 2001 From: kadmin Date: Fri, 30 Jul 2021 08:56:45 +0000 Subject: [PATCH 12/64] add eq constraints on associated constants --- src/types.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/types.rs b/src/types.rs index 88f5dc4324510..4bad9742a0ef4 100644 --- a/src/types.rs +++ b/src/types.rs @@ -141,7 +141,7 @@ pub(crate) enum SegmentParam<'a> { Const(&'a ast::AnonConst), LifeTime(&'a ast::Lifetime), Type(&'a ast::Ty), - Binding(&'a ast::AssocTyConstraint), + Binding(&'a ast::AssocConstraint), } impl<'a> SegmentParam<'a> { @@ -176,9 +176,9 @@ impl<'a> Rewrite for SegmentParam<'a> { } } -impl Rewrite for ast::AssocTyConstraint { +impl Rewrite for ast::AssocConstraint { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - use ast::AssocTyConstraintKind::{Bound, Equality}; + use ast::AssocConstraintKind::{Bound, Equality, ConstEquality}; let mut result = String::with_capacity(128); result.push_str(rewrite_ident(context, self.ident)); @@ -192,8 +192,8 @@ impl Rewrite for ast::AssocTyConstraint { let infix = match (&self.kind, context.config.type_punctuation_density()) { (Bound { .. }, _) => ": ", - (Equality { .. }, TypeDensity::Wide) => " = ", - (Equality { .. }, TypeDensity::Compressed) => "=", + (ConstEquality { .. } | Equality { .. }, TypeDensity::Wide) => " = ", + (ConstEquality { .. } | Equality { .. }, TypeDensity::Compressed) => "=", }; result.push_str(infix); @@ -206,11 +206,12 @@ impl Rewrite for ast::AssocTyConstraint { } } -impl Rewrite for ast::AssocTyConstraintKind { +impl Rewrite for ast::AssocConstraintKind { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { match self { - ast::AssocTyConstraintKind::Equality { ty } => ty.rewrite(context, shape), - ast::AssocTyConstraintKind::Bound { bounds } => bounds.rewrite(context, shape), + ast::AssocConstraintKind::Equality { ty } => ty.rewrite(context, shape), + ast::AssocConstraintKind::ConstEquality { c } => c.rewrite(context, shape), + ast::AssocConstraintKind::Bound { bounds } => bounds.rewrite(context, shape), } } } From cf86d532029bb27ef96780015a50613d2130102b Mon Sep 17 00:00:00 2001 From: kadmin Date: Fri, 7 Jan 2022 03:58:32 +0000 Subject: [PATCH 13/64] Add term Instead of having a separate enum variant for types and consts have one but have either a const or type. --- src/types.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/types.rs b/src/types.rs index 4bad9742a0ef4..9d5f790e80956 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,7 +1,7 @@ use std::iter::ExactSizeIterator; use std::ops::Deref; -use rustc_ast::ast::{self, FnRetTy, Mutability}; +use rustc_ast::ast::{self, FnRetTy, Mutability, Term}; use rustc_ast::ptr; use rustc_span::{symbol::kw, BytePos, Pos, Span}; @@ -178,7 +178,7 @@ impl<'a> Rewrite for SegmentParam<'a> { impl Rewrite for ast::AssocConstraint { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - use ast::AssocConstraintKind::{Bound, Equality, ConstEquality}; + use ast::AssocConstraintKind::{Bound, Equality}; let mut result = String::with_capacity(128); result.push_str(rewrite_ident(context, self.ident)); @@ -192,8 +192,8 @@ impl Rewrite for ast::AssocConstraint { let infix = match (&self.kind, context.config.type_punctuation_density()) { (Bound { .. }, _) => ": ", - (ConstEquality { .. } | Equality { .. }, TypeDensity::Wide) => " = ", - (ConstEquality { .. } | Equality { .. }, TypeDensity::Compressed) => "=", + (Equality { .. }, TypeDensity::Wide) => " = ", + (Equality { .. }, TypeDensity::Compressed) => "=", }; result.push_str(infix); @@ -209,8 +209,10 @@ impl Rewrite for ast::AssocConstraint { impl Rewrite for ast::AssocConstraintKind { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { match self { - ast::AssocConstraintKind::Equality { ty } => ty.rewrite(context, shape), - ast::AssocConstraintKind::ConstEquality { c } => c.rewrite(context, shape), + ast::AssocConstraintKind::Equality { term } => match term { + Term::Ty(ty) => ty.rewrite(context, shape), + Term::Const(c) => c.rewrite(context,shape), + }, ast::AssocConstraintKind::Bound { bounds } => bounds.rewrite(context, shape), } } From 7913f130d3ee0682916586c7b43664b651435594 Mon Sep 17 00:00:00 2001 From: kadmin Date: Thu, 13 Jan 2022 07:39:58 +0000 Subject: [PATCH 14/64] Add term to ExistentialProjection Also prevent ICE when adding a const in associated const equality. --- src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.rs b/src/types.rs index 9d5f790e80956..5de30129266a3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -211,7 +211,7 @@ impl Rewrite for ast::AssocConstraintKind { match self { ast::AssocConstraintKind::Equality { term } => match term { Term::Ty(ty) => ty.rewrite(context, shape), - Term::Const(c) => c.rewrite(context,shape), + Term::Const(c) => c.rewrite(context, shape), }, ast::AssocConstraintKind::Bound { bounds } => bounds.rewrite(context, shape), } From 9e1973f1d9b94914c3e5539f3ed992c19389b4a7 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 23 Jan 2022 11:18:17 -0600 Subject: [PATCH 15/64] chore: bump toolchain, update test --- rust-toolchain | 2 +- src/test/mod.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/rust-toolchain b/rust-toolchain index d4cdcec2018aa..d8bf02aec85e2 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-12-29" +channel = "nightly-2022-01-23" components = ["rustc-dev"] diff --git a/src/test/mod.rs b/src/test/mod.rs index c399512ba7e39..ab966d4a36075 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -572,7 +572,10 @@ fn stdin_generated_files_issue_5172() { assert!(session.has_no_errors()); } // N.B. this should be changed once `format_generated_files` is supported with stdin - assert_eq!(buf, "stdin:\n\n//@generated\nfn main() {}\n".as_bytes()); + assert_eq!( + String::from_utf8(buf).unwrap(), + ":\n\n//@generated\nfn main() {}\n", + ); } #[test] From 3572c542c29facef5073ba801213fb160c376b4a Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Mon, 24 Jan 2022 11:23:14 +0000 Subject: [PATCH 16/64] rustc_errors: only box the `diagnostic` field in `DiagnosticBuilder`. --- src/parse/session.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parse/session.rs b/src/parse/session.rs index 624fed0d2de26..7fc3778376cd0 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -315,7 +315,7 @@ mod tests { code: None, message: vec![], children: vec![], - suggestions: vec![], + suggestions: Ok(vec![]), span: span.unwrap_or_else(MultiSpan::new), sort_span: DUMMY_SP, is_lint: false, From b4a4bf0bf8a16913b9f16f2aee7030065ff00931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20Giba=C5=82a?= Date: Sat, 29 Jan 2022 05:55:47 +0100 Subject: [PATCH 17/64] Fix formatting of comments in empty structs (#5171) * Fix formatting of comments in empty structs * Add tests * Add single line tests * Fix block comments * Revert changes of test source files --- src/items.rs | 10 +- tests/source/issue_4854.rs | 113 +++++++++++++++++ .../comments-in-lists/format-doc-comments.rs | 10 +- .../comments-in-lists/wrap-comments-false.rs | 10 +- .../wrap-comments-not-normalized.rs | 8 +- .../comments-in-lists/wrap-comments-true.rs | 8 +- tests/target/issue_4854.rs | 115 ++++++++++++++++++ 7 files changed, 251 insertions(+), 23 deletions(-) create mode 100644 tests/source/issue_4854.rs create mode 100644 tests/target/issue_4854.rs diff --git a/src/items.rs b/src/items.rs index babc56f86edc6..4c7a33c86d2f0 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1374,17 +1374,21 @@ fn format_empty_struct_or_tuple( result.push_str(&offset.to_string_with_newline(context.config)) } result.push_str(opener); - match rewrite_missing_comment(span, Shape::indented(offset, context.config), context) { + + // indented shape for proper indenting of multi-line comments + let shape = Shape::indented(offset.block_indent(context.config), context.config); + match rewrite_missing_comment(span, shape, context) { Some(ref s) if s.is_empty() => (), Some(ref s) => { - if !is_single_line(s) || first_line_contains_single_line_comment(s) { + let is_multi_line = !is_single_line(s); + if is_multi_line || first_line_contains_single_line_comment(s) { let nested_indent_str = offset .block_indent(context.config) .to_string_with_newline(context.config); result.push_str(&nested_indent_str); } result.push_str(s); - if last_line_contains_single_line_comment(s) { + if is_multi_line || last_line_contains_single_line_comment(s) { result.push_str(&offset.to_string_with_newline(context.config)); } } diff --git a/tests/source/issue_4854.rs b/tests/source/issue_4854.rs new file mode 100644 index 0000000000000..35d6e21affeeb --- /dev/null +++ b/tests/source/issue_4854.rs @@ -0,0 +1,113 @@ +struct Struct { + // Multiline comment + // should be formatted + // properly. +} + +struct Struct2 { + // This formatting +// Should be changed +} + +struct Struct3( + // This + // is + // correct +); + +struct Struct4( + // This +// is +// not +// correct +); + +struct Struct5 { + /* + Comment block + with many lines. + */ +} + +struct Struct6( + /* + Comment block + with many lines. + */ +); + +struct Struct7 { + /* +Invalid +format +*/ +} + +struct Struct8( + /* +Invalid +format +*/ +); + +struct Struct9 { /* bar */ } + +struct Struct10 { /* bar +baz +*/ } + +mod module { + struct Struct { + // Multiline comment + // should be formatted + // properly. + } + + struct Struct2 { + // This formatting +// Should be changed + } + + struct Struct3( + // This + // is + // correct + ); + + struct Struct4( + // This + // is + // not +// correct + ); + + struct Struct5 { + /* + Comment block + with many lines. + */ + } + + struct Struct6( + /* + Comment block + with many lines. + */ + ); + + struct Struct7 { + /* +Invalid +format +*/ + } + + struct Struct8( + /* +Invalid +format +*/ + ); + + struct Struct9 { /* bar */ } +} diff --git a/tests/target/comments-in-lists/format-doc-comments.rs b/tests/target/comments-in-lists/format-doc-comments.rs index be31bf0a33198..be4b7a8c42e3e 100644 --- a/tests/target/comments-in-lists/format-doc-comments.rs +++ b/tests/target/comments-in-lists/format-doc-comments.rs @@ -25,9 +25,8 @@ pub enum E { } pub enum E2 { - // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed -// Expand as needed, numbers should be ascending according to the stage -// through the inclusion pipeline, or according to the descriptions + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions } pub struct S { @@ -42,9 +41,8 @@ pub struct S { } pub struct S2 { - // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed -// Expand as needed, numbers should be ascending according to the stage -// through the inclusion pipeline, or according to the descriptions + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions } fn foo( diff --git a/tests/target/comments-in-lists/wrap-comments-false.rs b/tests/target/comments-in-lists/wrap-comments-false.rs index 80aea59d1b520..db4da6223721c 100644 --- a/tests/target/comments-in-lists/wrap-comments-false.rs +++ b/tests/target/comments-in-lists/wrap-comments-false.rs @@ -13,9 +13,8 @@ pub enum E { } pub enum E2 { - // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed -// Expand as needed, numbers should be ascending according to the stage -// through the inclusion pipeline, or according to the descriptions + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions } pub struct S { @@ -30,9 +29,8 @@ pub struct S { } pub struct S2 { - // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed -// Expand as needed, numbers should be ascending according to the stage -// through the inclusion pipeline, or according to the descriptions + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions } fn foo( diff --git a/tests/target/comments-in-lists/wrap-comments-not-normalized.rs b/tests/target/comments-in-lists/wrap-comments-not-normalized.rs index 52315f470e4b9..9b9147eb1247a 100644 --- a/tests/target/comments-in-lists/wrap-comments-not-normalized.rs +++ b/tests/target/comments-in-lists/wrap-comments-not-normalized.rs @@ -14,8 +14,8 @@ pub enum E { pub enum E2 { // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed -// Expand as needed, numbers should be ascending according to the stage -// through the inclusion pipeline, or according to the descriptions + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions } pub enum E3 { @@ -42,8 +42,8 @@ pub struct S { pub struct S2 { // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed -// Expand as needed, numbers should be ascending according to the stage -// through the inclusion pipeline, or according to the descriptions + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions } pub struct S3 { diff --git a/tests/target/comments-in-lists/wrap-comments-true.rs b/tests/target/comments-in-lists/wrap-comments-true.rs index e0bfcf0b5007d..c1531d22a4a70 100644 --- a/tests/target/comments-in-lists/wrap-comments-true.rs +++ b/tests/target/comments-in-lists/wrap-comments-true.rs @@ -15,8 +15,8 @@ pub enum E { pub enum E2 { // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed -// Expand as needed, numbers should be ascending according to the stage -// through the inclusion pipeline, or according to the descriptions + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions } pub enum E3 { @@ -43,8 +43,8 @@ pub struct S { pub struct S2 { // This can be changed once https://github.com/rust-lang/rustfmt/issues/4854 is fixed -// Expand as needed, numbers should be ascending according to the stage -// through the inclusion pipeline, or according to the descriptions + // Expand as needed, numbers should be ascending according to the stage + // through the inclusion pipeline, or according to the descriptions } pub struct S3 { diff --git a/tests/target/issue_4854.rs b/tests/target/issue_4854.rs new file mode 100644 index 0000000000000..a81c5a5171fb5 --- /dev/null +++ b/tests/target/issue_4854.rs @@ -0,0 +1,115 @@ +struct Struct { + // Multiline comment + // should be formatted + // properly. +} + +struct Struct2 { + // This formatting + // Should be changed +} + +struct Struct3( + // This + // is + // correct +); + +struct Struct4( + // This + // is + // not + // correct +); + +struct Struct5 { + /* + Comment block + with many lines. + */ +} + +struct Struct6( + /* + Comment block + with many lines. + */ +); + +struct Struct7 { + /* + Invalid + format + */ +} + +struct Struct8( + /* + Invalid + format + */ +); + +struct Struct9 {/* bar */} + +struct Struct10 { + /* bar + baz + */ +} + +mod module { + struct Struct { + // Multiline comment + // should be formatted + // properly. + } + + struct Struct2 { + // This formatting + // Should be changed + } + + struct Struct3( + // This + // is + // correct + ); + + struct Struct4( + // This + // is + // not + // correct + ); + + struct Struct5 { + /* + Comment block + with many lines. + */ + } + + struct Struct6( + /* + Comment block + with many lines. + */ + ); + + struct Struct7 { + /* + Invalid + format + */ + } + + struct Struct8( + /* + Invalid + format + */ + ); + + struct Struct9 {/* bar */} +} From 8b0b213cddb23a9bbe421b717d1a0e5fb3982712 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Thu, 9 Dec 2021 23:17:43 -0500 Subject: [PATCH 18/64] Prevent adding trailing whitespace when rewriting ast::Param Fixes 5125 Previously, a newline was always added, even if the parameter name was not preceded by any param attrs. Now a newline is only added if there were param attrs. --- src/items.rs | 8 ++++++- .../attributes_in_formal_fuction_parameter.rs | 6 +++++ .../long_parameter_in_different_positions.rs | 24 +++++++++++++++++++ tests/target/issue-5125/minimum_example.rs | 6 +++++ .../with_leading_and_inline_comments.rs | 7 ++++++ 5 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 tests/target/issue-5125/attributes_in_formal_fuction_parameter.rs create mode 100644 tests/target/issue-5125/long_parameter_in_different_positions.rs create mode 100644 tests/target/issue-5125/minimum_example.rs create mode 100644 tests/target/issue-5125/with_leading_and_inline_comments.rs diff --git a/src/items.rs b/src/items.rs index 4c7a33c86d2f0..007609aba249f 100644 --- a/src/items.rs +++ b/src/items.rs @@ -2018,9 +2018,15 @@ impl Rewrite for ast::Param { { result.push_str(&ty_str); } else { + let prev_str = if param_attrs_result.is_empty() { + param_attrs_result + } else { + param_attrs_result + &shape.to_string_with_newline(context.config) + }; + result = combine_strs_with_missing_comments( context, - &(param_attrs_result + &shape.to_string_with_newline(context.config)), + &prev_str, param_name, span, shape, diff --git a/tests/target/issue-5125/attributes_in_formal_fuction_parameter.rs b/tests/target/issue-5125/attributes_in_formal_fuction_parameter.rs new file mode 100644 index 0000000000000..5d167932828f3 --- /dev/null +++ b/tests/target/issue-5125/attributes_in_formal_fuction_parameter.rs @@ -0,0 +1,6 @@ +fn foo( + #[unused] a: >::ForeignType, +) { +} diff --git a/tests/target/issue-5125/long_parameter_in_different_positions.rs b/tests/target/issue-5125/long_parameter_in_different_positions.rs new file mode 100644 index 0000000000000..cab20381ce8f7 --- /dev/null +++ b/tests/target/issue-5125/long_parameter_in_different_positions.rs @@ -0,0 +1,24 @@ +fn middle( + a: usize, + b: >::ForeignType, + c: bool, +) { +} + +fn last( + a: usize, + b: >::ForeignType, +) { +} + +fn first( + a: >::ForeignType, + b: usize, +) { +} diff --git a/tests/target/issue-5125/minimum_example.rs b/tests/target/issue-5125/minimum_example.rs new file mode 100644 index 0000000000000..8003e66968c78 --- /dev/null +++ b/tests/target/issue-5125/minimum_example.rs @@ -0,0 +1,6 @@ +fn foo( + a: >::ForeignType, +) { +} diff --git a/tests/target/issue-5125/with_leading_and_inline_comments.rs b/tests/target/issue-5125/with_leading_and_inline_comments.rs new file mode 100644 index 0000000000000..2340b2f3472e9 --- /dev/null +++ b/tests/target/issue-5125/with_leading_and_inline_comments.rs @@ -0,0 +1,7 @@ +fn foo( + // Pre Comment + a: >::ForeignType, // Inline comment +) { +} From 368a9b7cef25c22c3e836453e73d8584b251b578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Campinas?= Date: Wed, 2 Feb 2022 02:06:14 +0100 Subject: [PATCH 19/64] Handle non-ascii character at boundary (#5089) * Handle non-ascii character at boundary * Replace substraction underflow check with early termination --- src/string.rs | 5 ++++- tests/source/issue-5023.rs | 22 ++++++++++++++++++++++ tests/target/issue-5023.rs | 23 +++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/source/issue-5023.rs create mode 100644 tests/target/issue-5023.rs diff --git a/src/string.rs b/src/string.rs index 64ae15672df8f..b65aa5b33b241 100644 --- a/src/string.rs +++ b/src/string.rs @@ -278,6 +278,9 @@ fn break_string(max_width: usize, trim_end: bool, line_end: &str, input: &[&str] } cur_index }; + if max_width_index_in_input == 0 { + return SnippetState::EndOfInput(input.concat()); + } // Find the position in input for breaking the string if line_end.is_empty() @@ -301,7 +304,7 @@ fn break_string(max_width: usize, trim_end: bool, line_end: &str, input: &[&str] return if trim_end { SnippetState::LineEnd(input[..=url_index_end].concat(), index_plus_ws + 1) } else { - return SnippetState::LineEnd(input[..=index_plus_ws].concat(), index_plus_ws + 1); + SnippetState::LineEnd(input[..=index_plus_ws].concat(), index_plus_ws + 1) }; } diff --git a/tests/source/issue-5023.rs b/tests/source/issue-5023.rs new file mode 100644 index 0000000000000..ae1c723eff76a --- /dev/null +++ b/tests/source/issue-5023.rs @@ -0,0 +1,22 @@ +// rustfmt-wrap_comments: true + +/// A comment to test special unicode characters on boundaries +/// 是,是,是,是,是,是,是,是,是,是,是,是 it should break right here this goes to the next line +fn main() { + if xxx { + let xxx = xxx + .into_iter() + .filter(|(xxx, xxx)| { + if let Some(x) = Some(1) { + // xxxxxxxxxxxxxxxxxx, xxxxxxxxxxxx, xxxxxxxxxxxxxxxxxxxx xxx xxxxxxx, xxxxx xxx + // xxxxxxxxxx. xxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxx xxx xxxxxxx + // 是sdfadsdfxxxxxxxxx,sdfaxxxxxx_xxxxx_masdfaonxxx, + if false { + return true; + } + } + false + }) + .collect(); + } +} diff --git a/tests/target/issue-5023.rs b/tests/target/issue-5023.rs new file mode 100644 index 0000000000000..4e84c7d98427a --- /dev/null +++ b/tests/target/issue-5023.rs @@ -0,0 +1,23 @@ +// rustfmt-wrap_comments: true + +/// A comment to test special unicode characters on boundaries +/// 是,是,是,是,是,是,是,是,是,是,是,是 it should break right here +/// this goes to the next line +fn main() { + if xxx { + let xxx = xxx + .into_iter() + .filter(|(xxx, xxx)| { + if let Some(x) = Some(1) { + // xxxxxxxxxxxxxxxxxx, xxxxxxxxxxxx, xxxxxxxxxxxxxxxxxxxx xxx xxxxxxx, xxxxx xxx + // xxxxxxxxxx. xxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxx xxx xxxxxxx + // 是sdfadsdfxxxxxxxxx,sdfaxxxxxx_xxxxx_masdfaonxxx, + if false { + return true; + } + } + false + }) + .collect(); + } +} From 606894eb0b1dcc3a4616aff4b54dbd19a2a46ba5 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 19 Dec 2021 16:47:13 -0500 Subject: [PATCH 20/64] Retain trailing separator when extracting the last inline post comment Fixes 5042 Previously, trailing commas were removed from the last inline comment. This lead to rustfmt refusing to format code snippets because the original comment did not match the rewritten comment. Now, when rustfmt extracts the last inline comment it leaves trailing separators alone. Rustfmt does not need to remove these separators because they are commented out. --- src/lists.rs | 27 ++++++++++++++----- .../multi-line_comment_with_trailing_comma.rs | 24 +++++++++++++++++ ...lti-line_comment_without_trailing_comma.rs | 24 +++++++++++++++++ ...single-line_comment_with_trailing_comma.rs | 9 +++++++ ...gle-line_comment_without_trailing_comma.rs | 10 +++++++ .../multi-line_comment_with_trailing_comma.rs | 24 +++++++++++++++++ ...lti-line_comment_without_trailing_comma.rs | 24 +++++++++++++++++ ...single-line_comment_with_trailing_comma.rs | 7 +++++ ...gle-line_comment_without_trailing_comma.rs | 7 +++++ 9 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs create mode 100644 tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs create mode 100644 tests/source/issue-5042/single-line_comment_with_trailing_comma.rs create mode 100644 tests/source/issue-5042/single-line_comment_without_trailing_comma.rs create mode 100644 tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs create mode 100644 tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs create mode 100644 tests/target/issue-5042/single-line_comment_with_trailing_comma.rs create mode 100644 tests/target/issue-5042/single-line_comment_without_trailing_comma.rs diff --git a/src/lists.rs b/src/lists.rs index 7aa0315f18c26..97eea19f93210 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -611,15 +611,30 @@ pub(crate) fn extract_post_comment( post_snippet: &str, comment_end: usize, separator: &str, + is_last: bool, ) -> Option { let white_space: &[_] = &[' ', '\t']; // Cleanup post-comment: strip separators and whitespace. let post_snippet = post_snippet[..comment_end].trim(); + + let last_inline_comment_ends_with_separator = if is_last { + if let Some(line) = post_snippet.lines().last() { + line.ends_with(separator) && line.trim().starts_with("//") + } else { + false + } + } else { + false + }; + let post_snippet_trimmed = if post_snippet.starts_with(|c| c == ',' || c == ':') { post_snippet[1..].trim_matches(white_space) } else if let Some(stripped) = post_snippet.strip_prefix(separator) { stripped.trim_matches(white_space) + } else if last_inline_comment_ends_with_separator { + // since we're on the last item it's fine to keep any trailing separators in comments + post_snippet.trim_matches(white_space) } // not comment or over two lines else if post_snippet.ends_with(',') @@ -748,14 +763,12 @@ where .snippet_provider .span_to_snippet(mk_sp((self.get_hi)(&item), next_start)) .unwrap_or(""); - let comment_end = get_comment_end( - post_snippet, - self.separator, - self.terminator, - self.inner.peek().is_none(), - ); + let is_last = self.inner.peek().is_none(); + let comment_end = + get_comment_end(post_snippet, self.separator, self.terminator, is_last); let new_lines = has_extra_newline(post_snippet, comment_end); - let post_comment = extract_post_comment(post_snippet, comment_end, self.separator); + let post_comment = + extract_post_comment(post_snippet, comment_end, self.separator, is_last); self.prev_span_end = (self.get_hi)(&item) + BytePos(comment_end as u32); diff --git a/tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs b/tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs new file mode 100644 index 0000000000000..5d171f32a1aea --- /dev/null +++ b/tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs @@ -0,0 +1,24 @@ +fn main() { + // 5042 deals with trailing commas, not the indentation issue of these comments + // When a future PR fixes the inentation issues these test can be updated + let _ = std::ops::Add::add(10, 20 + // ... + // ..., + ); + + let _ = std::ops::Add::add(10, 20 + /* ... */ + // ..., + ); + + let _ = std::ops::Add::add(10, 20 + // ..., + // ..., + ); + + let _ = std::ops::Add::add(10, 20 + // ..., + /* ... + */, + ); +} diff --git a/tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs b/tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs new file mode 100644 index 0000000000000..b8a824b34b796 --- /dev/null +++ b/tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs @@ -0,0 +1,24 @@ +fn main() { + // 5042 deals with trailing commas, not the indentation issue of these comments + // When a future PR fixes the inentation issues these test can be updated + let _ = std::ops::Add::add(10, 20 + // ... + // ... + ); + + let _ = std::ops::Add::add(10, 20 + /* ... */ + // ... + ); + + let _ = std::ops::Add::add(10, 20 + // ... + // ... + ); + + let _ = std::ops::Add::add(10, 20 + // ... + /* ... + */ + ); +} diff --git a/tests/source/issue-5042/single-line_comment_with_trailing_comma.rs b/tests/source/issue-5042/single-line_comment_with_trailing_comma.rs new file mode 100644 index 0000000000000..bd765b7b41f49 --- /dev/null +++ b/tests/source/issue-5042/single-line_comment_with_trailing_comma.rs @@ -0,0 +1,9 @@ +fn main() { + let _ = std::ops::Add::add(10, 20 + // ..., + ); + + let _ = std::ops::Add::add(10, 20 + /* ... */, + ); +} diff --git a/tests/source/issue-5042/single-line_comment_without_trailing_comma.rs b/tests/source/issue-5042/single-line_comment_without_trailing_comma.rs new file mode 100644 index 0000000000000..2ed8de875add8 --- /dev/null +++ b/tests/source/issue-5042/single-line_comment_without_trailing_comma.rs @@ -0,0 +1,10 @@ +fn main() { + let _ = std::ops::Add::add(10, 20 + // ... + ); + + let _ = std::ops::Add::add(10, 20 + /* ... */ + ); +} + diff --git a/tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs b/tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs new file mode 100644 index 0000000000000..1ae1212b488d9 --- /dev/null +++ b/tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs @@ -0,0 +1,24 @@ +fn main() { + // 5042 deals with trailing commas, not the indentation issue of these comments + // When a future PR fixes the inentation issues these test can be updated + let _ = std::ops::Add::add( + 10, 20, // ... + // ..., + ); + + let _ = std::ops::Add::add( + 10, 20, /* ... */ + // ..., + ); + + let _ = std::ops::Add::add( + 10, 20, // ..., + // ..., + ); + + let _ = std::ops::Add::add( + 10, 20, // ..., + /* ... + */ + ); +} diff --git a/tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs b/tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs new file mode 100644 index 0000000000000..30d174664c9cf --- /dev/null +++ b/tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs @@ -0,0 +1,24 @@ +fn main() { + // 5042 deals with trailing commas, not the indentation issue of these comments + // When a future PR fixes the inentation issues these test can be updated + let _ = std::ops::Add::add( + 10, 20, // ... + // ... + ); + + let _ = std::ops::Add::add( + 10, 20, /* ... */ + // ... + ); + + let _ = std::ops::Add::add( + 10, 20, // ... + // ... + ); + + let _ = std::ops::Add::add( + 10, 20, // ... + /* ... + */ + ); +} diff --git a/tests/target/issue-5042/single-line_comment_with_trailing_comma.rs b/tests/target/issue-5042/single-line_comment_with_trailing_comma.rs new file mode 100644 index 0000000000000..87b651dd285ef --- /dev/null +++ b/tests/target/issue-5042/single-line_comment_with_trailing_comma.rs @@ -0,0 +1,7 @@ +fn main() { + let _ = std::ops::Add::add( + 10, 20, // ..., + ); + + let _ = std::ops::Add::add(10, 20 /* ... */); +} diff --git a/tests/target/issue-5042/single-line_comment_without_trailing_comma.rs b/tests/target/issue-5042/single-line_comment_without_trailing_comma.rs new file mode 100644 index 0000000000000..116df86a4b554 --- /dev/null +++ b/tests/target/issue-5042/single-line_comment_without_trailing_comma.rs @@ -0,0 +1,7 @@ +fn main() { + let _ = std::ops::Add::add( + 10, 20, // ... + ); + + let _ = std::ops::Add::add(10, 20 /* ... */); +} From b2c7a52ea865f877bc0b57075fe1a88f83120546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Campinas?= Date: Mon, 31 Jan 2022 23:45:30 +0100 Subject: [PATCH 21/64] Fix import_granularity option when the use tree has an alias --- src/imports.rs | 6 ++++-- tests/source/5131.rs | 33 +++++++++++++++++++++++++++++++++ tests/target/5131.rs | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 tests/source/5131.rs create mode 100644 tests/target/5131.rs diff --git a/src/imports.rs b/src/imports.rs index 40e0d06f99df8..c60bec6d4a201 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -238,7 +238,8 @@ impl fmt::Display for UseSegment { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { UseSegment::Glob => write!(f, "*"), - UseSegment::Ident(ref s, _) => write!(f, "{}", s), + UseSegment::Ident(ref s, Some(ref alias)) => write!(f, "{} as {}", s, alias), + UseSegment::Ident(ref s, None) => write!(f, "{}", s), UseSegment::Slf(..) => write!(f, "self"), UseSegment::Super(..) => write!(f, "super"), UseSegment::Crate(..) => write!(f, "crate"), @@ -622,7 +623,8 @@ impl UseTree { fn merge(&mut self, other: &UseTree, merge_by: SharedPrefix) { let mut prefix = 0; for (a, b) in self.path.iter().zip(other.path.iter()) { - if a.equal_except_alias(b) { + // only discard the alias at the root of the tree + if (prefix == 0 && a.equal_except_alias(b)) || a == b { prefix += 1; } else { break; diff --git a/tests/source/5131.rs b/tests/source/5131.rs new file mode 100644 index 0000000000000..3e9139177c56e --- /dev/null +++ b/tests/source/5131.rs @@ -0,0 +1,33 @@ +// rustfmt-imports_granularity: Module + +#![allow(dead_code)] + +mod a { + pub mod b { + pub struct Data { + pub a: i32, + } + } + + use crate::a::b::Data; + use crate::a::b::Data as Data2; + + pub fn data(a: i32) -> Data { + Data { a } + } + + pub fn data2(a: i32) -> Data2 { + Data2 { a } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + pub fn test() { + data(1); + data2(1); + } + } +} diff --git a/tests/target/5131.rs b/tests/target/5131.rs new file mode 100644 index 0000000000000..763024d6fa495 --- /dev/null +++ b/tests/target/5131.rs @@ -0,0 +1,32 @@ +// rustfmt-imports_granularity: Module + +#![allow(dead_code)] + +mod a { + pub mod b { + pub struct Data { + pub a: i32, + } + } + + use crate::a::b::{Data, Data as Data2}; + + pub fn data(a: i32) -> Data { + Data { a } + } + + pub fn data2(a: i32) -> Data2 { + Data2 { a } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + pub fn test() { + data(1); + data2(1); + } + } +} From fd6e11cc57da5728a9a52c0a515fd80e811a43d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Campinas?= Date: Wed, 2 Feb 2022 15:43:31 +0100 Subject: [PATCH 22/64] Add tests for the One and Crate variants --- tests/source/5131_crate.rs | 14 ++++++++++++++ tests/source/{5131.rs => 5131_module.rs} | 0 tests/source/5131_one.rs | 15 +++++++++++++++ tests/target/5131_crate.rs | 9 +++++++++ tests/target/{5131.rs => 5131_module.rs} | 0 tests/target/5131_one.rs | 12 ++++++++++++ 6 files changed, 50 insertions(+) create mode 100644 tests/source/5131_crate.rs rename tests/source/{5131.rs => 5131_module.rs} (100%) create mode 100644 tests/source/5131_one.rs create mode 100644 tests/target/5131_crate.rs rename tests/target/{5131.rs => 5131_module.rs} (100%) create mode 100644 tests/target/5131_one.rs diff --git a/tests/source/5131_crate.rs b/tests/source/5131_crate.rs new file mode 100644 index 0000000000000..96a31659022aa --- /dev/null +++ b/tests/source/5131_crate.rs @@ -0,0 +1,14 @@ +// rustfmt-imports_granularity: Crate + +use foo::a; +use foo::a; +use foo::b; +use foo::b as b2; +use foo::b::f; +use foo::b::g; +use foo::b::g as g2; +use foo::c; +use foo::d::e; +use qux::h; +use qux::h as h2; +use qux::i; diff --git a/tests/source/5131.rs b/tests/source/5131_module.rs similarity index 100% rename from tests/source/5131.rs rename to tests/source/5131_module.rs diff --git a/tests/source/5131_one.rs b/tests/source/5131_one.rs new file mode 100644 index 0000000000000..61ddf13410d4b --- /dev/null +++ b/tests/source/5131_one.rs @@ -0,0 +1,15 @@ +// rustfmt-imports_granularity: One + +pub use foo::x; +pub use foo::x as x2; +pub use foo::y; +use bar::a; +use bar::b; +use bar::b::f; +use bar::b::f as f2; +use bar::b::g; +use bar::c; +use bar::d::e; +use bar::d::e as e2; +use qux::h; +use qux::i; diff --git a/tests/target/5131_crate.rs b/tests/target/5131_crate.rs new file mode 100644 index 0000000000000..557d667035546 --- /dev/null +++ b/tests/target/5131_crate.rs @@ -0,0 +1,9 @@ +// rustfmt-imports_granularity: Crate + +use foo::{ + a, b, b as b2, + b::{f, g, g as g2}, + c, + d::e, +}; +use qux::{h, h as h2, i}; diff --git a/tests/target/5131.rs b/tests/target/5131_module.rs similarity index 100% rename from tests/target/5131.rs rename to tests/target/5131_module.rs diff --git a/tests/target/5131_one.rs b/tests/target/5131_one.rs new file mode 100644 index 0000000000000..a086dae5a4221 --- /dev/null +++ b/tests/target/5131_one.rs @@ -0,0 +1,12 @@ +// rustfmt-imports_granularity: One + +pub use foo::{x, x as x2, y}; +use { + bar::{ + a, + b::{self, f, g}, + c, + d::{e, e as e2}, + }, + qux::{h, i}, +}; From 5df8c8f7e554e036fcb14a2d93d145c92d56bc2e Mon Sep 17 00:00:00 2001 From: Frank King Date: Mon, 7 Feb 2022 10:57:39 +0800 Subject: [PATCH 23/64] Fix doc of generic items formmating error (#5124) * Fix doc of generic items formmating error * Remove tracked `attrs_end_with_doc_comment` flag in `RewriteContext` * Fix duplicated doc comments of const generic params * Fix `::span()` * Remove duplicated source file of `doc-of-generic-item.rs` --- src/spanned.rs | 15 ++++----------- src/types.rs | 11 ++++++++++- tests/target/doc-of-generic-item.rs | 14 ++++++++++++++ 3 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 tests/target/doc-of-generic-item.rs diff --git a/src/spanned.rs b/src/spanned.rs index 8e6c75a3744ac..2136cfeae1af1 100644 --- a/src/spanned.rs +++ b/src/spanned.rs @@ -113,17 +113,10 @@ impl Spanned for ast::Param { impl Spanned for ast::GenericParam { fn span(&self) -> Span { - let lo = if let ast::GenericParamKind::Const { - ty: _, - kw_span, - default: _, - } = self.kind - { - kw_span.lo() - } else if self.attrs.is_empty() { - self.ident.span.lo() - } else { - self.attrs[0].span.lo() + let lo = match self.kind { + _ if !self.attrs.is_empty() => self.attrs[0].span.lo(), + ast::GenericParamKind::Const { kw_span, .. } => kw_span.lo(), + _ => self.ident.span.lo(), }; let hi = if self.bounds.is_empty() { self.ident.span.hi() diff --git a/src/types.rs b/src/types.rs index 5de30129266a3..b13e75a93b355 100644 --- a/src/types.rs +++ b/src/types.rs @@ -575,7 +575,16 @@ impl Rewrite for ast::GenericParam { let mut result = String::with_capacity(128); // FIXME: If there are more than one attributes, this will force multiline. match self.attrs.rewrite(context, shape) { - Some(ref rw) if !rw.is_empty() => result.push_str(&format!("{} ", rw)), + Some(ref rw) if !rw.is_empty() => { + result.push_str(rw); + // When rewriting generic params, an extra newline should be put + // if the attributes end with a doc comment + if let Some(true) = self.attrs.last().map(|a| a.is_doc_comment()) { + result.push_str(&shape.indent.to_string_with_newline(context.config)); + } else { + result.push(' '); + } + } _ => (), } diff --git a/tests/target/doc-of-generic-item.rs b/tests/target/doc-of-generic-item.rs new file mode 100644 index 0000000000000..2efc5e09a3d34 --- /dev/null +++ b/tests/target/doc-of-generic-item.rs @@ -0,0 +1,14 @@ +// Non-doc pre-comment of Foo +/// doc of Foo +// Non-doc post-comment of Foo +struct Foo< + // Non-doc pre-comment of 'a + /// doc of 'a + 'a, + // Non-doc pre-comment of T + /// doc of T + T, + // Non-doc pre-comment of N + /// doc of N + const N: item, +>; From ace7241087bedd2ea26d7b1dfcdd6133cbf77ce7 Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sat, 29 Jan 2022 16:44:49 -0500 Subject: [PATCH 24/64] Fix incorrect string indentation in macro defs with `format_strings` --- CHANGELOG.md | 4 ++++ src/utils.rs | 19 ++++++++++++++++--- tests/source/issue-4036/one.rs | 11 +++++++++++ tests/source/issue-4036/three.rs | 12 ++++++++++++ tests/source/issue-4036/two.rs | 11 +++++++++++ tests/target/issue-4036/one.rs | 12 ++++++++++++ tests/target/issue-4036/three.rs | 17 +++++++++++++++++ tests/target/issue-4036/two.rs | 16 ++++++++++++++++ 8 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 tests/source/issue-4036/one.rs create mode 100644 tests/source/issue-4036/three.rs create mode 100644 tests/source/issue-4036/two.rs create mode 100644 tests/target/issue-4036/one.rs create mode 100644 tests/target/issue-4036/three.rs create mode 100644 tests/target/issue-4036/two.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index b59438dc4fe78..1b9208d31e000 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixes issue where wrapped strings would be incorrectly indented in macro defs when `format_strings` was enabled [#4036](https://github.com/rust-lang/rustfmt/issues/4036) + ## [1.4.38] 2021-10-20 ### Changed diff --git a/src/utils.rs b/src/utils.rs index 2428d8cb0fd89..35512e78fa6e2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -646,9 +646,22 @@ pub(crate) fn trim_left_preserve_layout( } /// Based on the given line, determine if the next line can be indented or not. -/// This allows to preserve the indentation of multi-line literals. -pub(crate) fn indent_next_line(kind: FullCodeCharKind, _line: &str, config: &Config) -> bool { - !(kind.is_string() || (config.version() == Version::Two && kind.is_commented_string())) +/// This allows to preserve the indentation of multi-line literals when +/// re-inserted a code block that has been formatted separately from the rest +/// of the code, such as code in macro defs or code blocks doc comments. +pub(crate) fn indent_next_line(kind: FullCodeCharKind, line: &str, config: &Config) -> bool { + if kind.is_string() { + // If the string ends with '\', the string has been wrapped over + // multiple lines. If `format_strings = true`, then the indentation of + // strings wrapped over multiple lines will have been adjusted while + // formatting the code block, therefore the string's indentation needs + // to be adjusted for the code surrounding the code block. + config.format_strings() && line.ends_with('\\') + } else if config.version() == Version::Two { + !kind.is_commented_string() + } else { + true + } } pub(crate) fn is_empty_line(s: &str) -> bool { diff --git a/tests/source/issue-4036/one.rs b/tests/source/issue-4036/one.rs new file mode 100644 index 0000000000000..9f9675f51631a --- /dev/null +++ b/tests/source/issue-4036/one.rs @@ -0,0 +1,11 @@ +// rustfmt-format_strings: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "We asserted that `buffer.len()` is exactly `$n` so we can expect `ApInt::from_iter` to be successful.", + ) + } + }; +} diff --git a/tests/source/issue-4036/three.rs b/tests/source/issue-4036/three.rs new file mode 100644 index 0000000000000..e1865dd0868b6 --- /dev/null +++ b/tests/source/issue-4036/three.rs @@ -0,0 +1,12 @@ +// rustfmt-format_strings: true +// rustfmt-hard_tabs: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + ) + } + }; +} diff --git a/tests/source/issue-4036/two.rs b/tests/source/issue-4036/two.rs new file mode 100644 index 0000000000000..fa54d2e3e09cb --- /dev/null +++ b/tests/source/issue-4036/two.rs @@ -0,0 +1,11 @@ +// rustfmt-format_strings: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + ) + } + }; +} diff --git a/tests/target/issue-4036/one.rs b/tests/target/issue-4036/one.rs new file mode 100644 index 0000000000000..54e490b7fbeae --- /dev/null +++ b/tests/target/issue-4036/one.rs @@ -0,0 +1,12 @@ +// rustfmt-format_strings: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "We asserted that `buffer.len()` is exactly `$n` so we can expect \ + `ApInt::from_iter` to be successful.", + ) + } + }; +} diff --git a/tests/target/issue-4036/three.rs b/tests/target/issue-4036/three.rs new file mode 100644 index 0000000000000..394dc8633f534 --- /dev/null +++ b/tests/target/issue-4036/three.rs @@ -0,0 +1,17 @@ +// rustfmt-format_strings: true +// rustfmt-hard_tabs: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor \ + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis \ + nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \ + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu \ + fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in \ + culpa qui officia deserunt mollit anim id est laborum.", + ) + } + }; +} diff --git a/tests/target/issue-4036/two.rs b/tests/target/issue-4036/two.rs new file mode 100644 index 0000000000000..01cafa76b6842 --- /dev/null +++ b/tests/target/issue-4036/two.rs @@ -0,0 +1,16 @@ +// rustfmt-format_strings: true + +macro_rules! test { + () => { + fn from() { + None.expect( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor \ + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis \ + nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \ + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu \ + fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in \ + culpa qui officia deserunt mollit anim id est laborum.", + ) + } + }; +} From 813d127c824eb22e3fe1fc7a34bf12ad4c0fa2f6 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Mon, 7 Feb 2022 04:39:53 +0000 Subject: [PATCH 25/64] Clarify generated marker requires a config option --- CHANGELOG.md | 1 + Configurations.md | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b9208d31e000..5b23360858356 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ Note this hit the rustup distributions prior to the v1.4.38 release as part of a - New `One` variant added to `imports_granularity` configuration option which can be used to reformat all imports into a single use statement [#4669](https://github.com/rust-lang/rustfmt/issues/4669) - rustfmt will now skip files that are annotated with `@generated` at the top of the file [#3958](https://github.com/rust-lang/rustfmt/issues/3958) + if `format_generated_files` option is set to `false` (by default `@generated` files are formatted) - New configuration option `hex_literal_case` that allows user to control the casing utilized for hex literals [PR #4903](https://github.com/rust-lang/rustfmt/pull/4903) See the section on the configuration site for more information diff --git a/Configurations.md b/Configurations.md index 4476f2a449b1f..2e2b0f7cfbecf 100644 --- a/Configurations.md +++ b/Configurations.md @@ -930,6 +930,8 @@ fn add_one(x: i32) -> i32 { Format generated files. A file is considered generated if any of the first five lines contain a `@generated` comment marker. +By default, generated files are reformatted, i. e. `@generated` marker is ignored. +This option is currently ignored for stdin (`@generated` in stdin is ignored.) - **Default value**: `true` - **Possible values**: `true`, `false` From b05b3138001c97cbabeb3e20a0960b9f75341ee0 Mon Sep 17 00:00:00 2001 From: Tharun Rajendran Date: Fri, 11 Feb 2022 10:05:45 +0530 Subject: [PATCH 26/64] chore(rustfmt): remove executable path from usage string (#5216) * chore(rustfmt): remove executable path from usage string * add unit test for usage string * rename test and check usage text in a single assert --- src/bin/main.rs | 5 ++--- tests/rustfmt/main.rs | 9 +++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 6f5b09fc86adf..196de6056b5ce 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -394,9 +394,8 @@ fn print_usage_to_stdout(opts: &Options, reason: &str) { format!("{}\n\n", reason) }; let msg = format!( - "{}Format Rust code\n\nusage: {} [options] ...", - sep, - env::args_os().next().unwrap().to_string_lossy() + "{}Format Rust code\n\nusage: rustfmt [options] ...", + sep ); println!("{}", opts.usage(&msg)); } diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs index 8effb1c6fcab6..6976cc4d346cb 100644 --- a/tests/rustfmt/main.rs +++ b/tests/rustfmt/main.rs @@ -106,3 +106,12 @@ fn inline_config() { && contains("format_strings = true") ); } + +#[test] +fn rustfmt_usage_text() { + let args = [ + "--help", + ]; + let (stdout, _) = rustfmt(&args); + assert!(stdout.contains(&format!("Format Rust code\n\nusage: rustfmt [options] ..."))); +} From 1e78a2b258470fcab13e8c933212667e64b21d3e Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 29 Dec 2021 19:59:54 -0500 Subject: [PATCH 27/64] Leverage itemized blocks to support formatting markdown block quotes Fixes 5157 Doc comments support markdown, but rustfmt didn't previously assign any semantic value to leading '> ' in comments. This lead to poor formatting when using ``wrap_comments=true``. Now, rustfmt treats block quotes as itemized blocks, which greatly improves how block quotes are formatted when ``wrap_comments=true``. --- src/comment.rs | 46 ++++++++++++++++--- .../indented_itemized_markdown_blockquote.rs | 4 ++ .../nested_itemized_markdown_blockquote.rs | 10 ++++ .../support_itemized_markdown_blockquote.rs | 4 ++ .../indented_itemized_markdown_blockquote.rs | 6 +++ .../nested_itemized_markdown_blockquote.rs | 18 ++++++++ .../support_itemized_markdown_blockquote.rs | 6 +++ 7 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 tests/source/issue-5157/indented_itemized_markdown_blockquote.rs create mode 100644 tests/source/issue-5157/nested_itemized_markdown_blockquote.rs create mode 100644 tests/source/issue-5157/support_itemized_markdown_blockquote.rs create mode 100644 tests/target/issue-5157/indented_itemized_markdown_blockquote.rs create mode 100644 tests/target/issue-5157/nested_itemized_markdown_blockquote.rs create mode 100644 tests/target/issue-5157/support_itemized_markdown_blockquote.rs diff --git a/src/comment.rs b/src/comment.rs index 0f850b9b2f2fb..96778c4ef1ddb 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -432,16 +432,16 @@ impl CodeBlockAttribute { /// Block that is formatted as an item. /// -/// An item starts with either a star `*` or a dash `-`. Different level of indentation are -/// handled by shrinking the shape accordingly. +/// An item starts with either a star `*` a dash `-` or a greater-than `>`. +/// Different level of indentation are handled by shrinking the shape accordingly. struct ItemizedBlock { /// the lines that are identified as part of an itemized block lines: Vec, - /// the number of whitespaces up to the item sigil + /// the number of characters (typically whitespaces) up to the item sigil indent: usize, /// the string that marks the start of an item opener: String, - /// sequence of whitespaces to prefix new lines that are part of the item + /// sequence of characters (typically whitespaces) to prefix new lines that are part of the item line_start: String, } @@ -449,19 +449,32 @@ impl ItemizedBlock { /// Returns `true` if the line is formatted as an item fn is_itemized_line(line: &str) -> bool { let trimmed = line.trim_start(); - trimmed.starts_with("* ") || trimmed.starts_with("- ") + trimmed.starts_with("* ") || trimmed.starts_with("- ") || trimmed.starts_with("> ") } /// Creates a new ItemizedBlock described with the given line. /// The `is_itemized_line` needs to be called first. fn new(line: &str) -> ItemizedBlock { let space_to_sigil = line.chars().take_while(|c| c.is_whitespace()).count(); - let indent = space_to_sigil + 2; + // +2 = '* ', which will add the appropriate amount of whitespace to keep itemized + // content formatted correctly. + let mut indent = space_to_sigil + 2; + let mut line_start = " ".repeat(indent); + + // Markdown blockquote start with a "> " + if line.trim_start().starts_with(">") { + // remove the original +2 indent because there might be multiple nested block quotes + // and it's easier to reason about the final indent by just taking the length + // of th new line_start. We update the indent because it effects the max width + // of each formatted line. + line_start = itemized_block_quote_start(line, line_start, 2); + indent = line_start.len(); + } ItemizedBlock { lines: vec![line[indent..].to_string()], indent, opener: line[..indent].to_string(), - line_start: " ".repeat(indent), + line_start, } } @@ -504,6 +517,25 @@ impl ItemizedBlock { } } +/// Determine the line_start when formatting markdown block quotes. +/// The original line_start likely contains indentation (whitespaces), which we'd like to +/// replace with '> ' characters. +fn itemized_block_quote_start(line: &str, mut line_start: String, remove_indent: usize) -> String { + let quote_level = line + .chars() + .take_while(|c| !c.is_alphanumeric()) + .fold(0, |acc, c| if c == '>' { acc + 1 } else { acc }); + + for _ in 0..remove_indent { + line_start.pop(); + } + + for _ in 0..quote_level { + line_start.push_str("> ") + } + line_start +} + struct CommentRewrite<'a> { result: String, code_block_buffer: String, diff --git a/tests/source/issue-5157/indented_itemized_markdown_blockquote.rs b/tests/source/issue-5157/indented_itemized_markdown_blockquote.rs new file mode 100644 index 0000000000000..5c1d79a743098 --- /dev/null +++ b/tests/source/issue-5157/indented_itemized_markdown_blockquote.rs @@ -0,0 +1,4 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +fn block_quote() {} diff --git a/tests/source/issue-5157/nested_itemized_markdown_blockquote.rs b/tests/source/issue-5157/nested_itemized_markdown_blockquote.rs new file mode 100644 index 0000000000000..cf200d04e08ef --- /dev/null +++ b/tests/source/issue-5157/nested_itemized_markdown_blockquote.rs @@ -0,0 +1,10 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +/// +/// > > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +/// +/// > > > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +/// +/// > > > > > > > > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +fn block_quote() {} diff --git a/tests/source/issue-5157/support_itemized_markdown_blockquote.rs b/tests/source/issue-5157/support_itemized_markdown_blockquote.rs new file mode 100644 index 0000000000000..eb436402e4e00 --- /dev/null +++ b/tests/source/issue-5157/support_itemized_markdown_blockquote.rs @@ -0,0 +1,4 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ. +fn block_quote() {} diff --git a/tests/target/issue-5157/indented_itemized_markdown_blockquote.rs b/tests/target/issue-5157/indented_itemized_markdown_blockquote.rs new file mode 100644 index 0000000000000..e47677f203903 --- /dev/null +++ b/tests/target/issue-5157/indented_itemized_markdown_blockquote.rs @@ -0,0 +1,6 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a +/// > sample_state relative to each DataReader. The sample_state can +/// > either be READ or NOT_READ. +fn block_quote() {} diff --git a/tests/target/issue-5157/nested_itemized_markdown_blockquote.rs b/tests/target/issue-5157/nested_itemized_markdown_blockquote.rs new file mode 100644 index 0000000000000..079510442b799 --- /dev/null +++ b/tests/target/issue-5157/nested_itemized_markdown_blockquote.rs @@ -0,0 +1,18 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a +/// > sample_state relative to each DataReader. The sample_state can either be +/// > READ or NOT_READ. +/// +/// > > For each sample received, the middleware internally maintains a +/// > > sample_state relative to each DataReader. The sample_state can either be +/// > > READ or NOT_READ. +/// +/// > > > For each sample received, the middleware internally maintains a +/// > > > sample_state relative to each DataReader. The sample_state can either +/// > > > be READ or NOT_READ. +/// +/// > > > > > > > > For each sample received, the middleware internally +/// > > > > > > > > maintains a sample_state relative to each DataReader. The +/// > > > > > > > > sample_state can either be READ or NOT_READ. +fn block_quote() {} diff --git a/tests/target/issue-5157/support_itemized_markdown_blockquote.rs b/tests/target/issue-5157/support_itemized_markdown_blockquote.rs new file mode 100644 index 0000000000000..029ee37d22a8b --- /dev/null +++ b/tests/target/issue-5157/support_itemized_markdown_blockquote.rs @@ -0,0 +1,6 @@ +// rustfmt-wrap_comments: true + +/// > For each sample received, the middleware internally maintains a +/// > sample_state relative to each DataReader. The sample_state can either be +/// > READ or NOT_READ. +fn block_quote() {} From 6c476127aedc37b43769469f87e0fbc382382739 Mon Sep 17 00:00:00 2001 From: Travis Finkenauer Date: Tue, 15 Feb 2022 15:25:44 -0800 Subject: [PATCH 28/64] Add context to get_toml_path() error (#5207) * rustfmt: print full error chain * Add context to get_toml_path() error Instead of an error like: ``` Permission denied (os error 13) ``` Gives error like: ``` Failed to get metadata for config file "/root/.rustfmt.toml": Permission denied (os error 13) ``` --- src/bin/main.rs | 2 +- src/config/mod.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 196de6056b5ce..ad10b9ede608f 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -26,7 +26,7 @@ fn main() { let exit_code = match execute(&opts) { Ok(code) => code, Err(e) => { - eprintln!("{}", e); + eprintln!("{:#}", e); 1 } }; diff --git a/src/config/mod.rs b/src/config/mod.rs index cd90e0904b6cd..5041e1e36dd6d 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -364,7 +364,9 @@ fn get_toml_path(dir: &Path) -> Result, Error> { // find the project file yet, and continue searching. Err(e) => { if e.kind() != ErrorKind::NotFound { - return Err(e); + let ctx = format!("Failed to get metadata for config file {:?}", &config_file); + let err = anyhow::Error::new(e).context(ctx); + return Err(Error::new(ErrorKind::Other, err)); } } _ => {} From d5aabccfebc645d9fce62f91512b03ca0fa1fb6d Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 1 Feb 2022 13:38:54 -0600 Subject: [PATCH 29/64] Format code --- config_proc_macro/src/utils.rs | 4 ++-- tests/cargo-fmt/main.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config_proc_macro/src/utils.rs b/config_proc_macro/src/utils.rs index 5b68d2748490f..f5cba87b07b67 100644 --- a/config_proc_macro/src/utils.rs +++ b/config_proc_macro/src/utils.rs @@ -22,10 +22,10 @@ pub fn is_unit(v: &syn::Variant) -> bool { #[cfg(feature = "debug-with-rustfmt")] /// Pretty-print the output of proc macro using rustfmt. pub fn debug_with_rustfmt(input: &TokenStream) { - use std::io::Write; - use std::process::{Command, Stdio}; use std::env; use std::ffi::OsStr; + use std::io::Write; + use std::process::{Command, Stdio}; let rustfmt_var = env::var_os("RUSTFMT"); let rustfmt = match &rustfmt_var { diff --git a/tests/cargo-fmt/main.rs b/tests/cargo-fmt/main.rs index bf81f253f6913..3713552c66af9 100644 --- a/tests/cargo-fmt/main.rs +++ b/tests/cargo-fmt/main.rs @@ -1,8 +1,8 @@ // Integration tests for cargo-fmt. use std::env; -use std::process::Command; use std::path::Path; +use std::process::Command; /// Run the cargo-fmt executable and return its output. fn cargo_fmt(args: &[&str]) -> (String, String) { From c63d42e80473a0c18714b55058f27506fd24955c Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 1 Feb 2022 13:39:01 -0600 Subject: [PATCH 30/64] Use cargo-fmt in self_tests --- src/test/mod.rs | 46 ++++++++++++---------------------------------- 1 file changed, 12 insertions(+), 34 deletions(-) diff --git a/src/test/mod.rs b/src/test/mod.rs index ab966d4a36075..4191e3e96b0a8 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -375,43 +375,21 @@ fn idempotence_tests() { }); } -// Run rustfmt on itself. This operation must be idempotent. We also check that -// no warnings are emitted. -// Issue-3443: these tests require nightly #[nightly_only_test] #[test] fn self_tests() { - init_log(); - let mut files = get_test_files(Path::new("tests"), false); - let bin_directories = vec!["cargo-fmt", "git-rustfmt", "bin", "format-diff"]; - for dir in bin_directories { - let mut path = PathBuf::from("src"); - path.push(dir); - path.push("main.rs"); - files.push(path); - } - files.push(PathBuf::from("src/lib.rs")); - - let (reports, count, fails) = check_files(files, &Some(PathBuf::from("rustfmt.toml"))); - let mut warnings = 0; - - // Display results. - println!("Ran {} self tests.", count); - assert_eq!(fails, 0, "{} self tests failed", fails); - - for format_report in reports { - println!( - "{}", - FormatReportFormatterBuilder::new(&format_report).build() - ); - warnings += format_report.warning_count(); - } - - assert_eq!( - warnings, 0, - "Rustfmt's code generated {} warnings", - warnings - ); + let get_exe_path = |name| { + let mut path = env::current_exe().unwrap(); + path.pop(); + path.set_file_name(format!("{name}{}", env::consts::EXE_SUFFIX)); + path + }; + let status = Command::new(get_exe_path("cargo-fmt")) + .args(["--check", "--all"]) + .env("RUSTFMT", get_exe_path("rustfmt")) + .status() + .unwrap(); + assert!(status.success()); } #[test] From 281bf03e6492bf2627c24ea502ce5fd567d7a08e Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Tue, 15 Feb 2022 20:49:36 -0600 Subject: [PATCH 31/64] fix: formatting in new test --- tests/rustfmt/main.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs index 6976cc4d346cb..2262ae3aaacd0 100644 --- a/tests/rustfmt/main.rs +++ b/tests/rustfmt/main.rs @@ -109,9 +109,7 @@ fn inline_config() { #[test] fn rustfmt_usage_text() { - let args = [ - "--help", - ]; + let args = ["--help"]; let (stdout, _) = rustfmt(&args); - assert!(stdout.contains(&format!("Format Rust code\n\nusage: rustfmt [options] ..."))); + assert!(stdout.contains("Format Rust code\n\nusage: rustfmt [options] ...")); } From 7592663e85fe82de5f346a77a22772230b1378a1 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Sun, 23 Jan 2022 23:11:37 +0000 Subject: [PATCH 32/64] rustc_errors: add `downgrade_to_delayed_bug` to `Diagnostic` itself. --- src/parse/session.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/parse/session.rs b/src/parse/session.rs index 7fc3778376cd0..d26bb8c20255b 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -60,7 +60,7 @@ impl Emitter for SilentOnIgnoredFilesEmitter { None } fn emit_diagnostic(&mut self, db: &Diagnostic) { - if db.level == DiagnosticLevel::Fatal { + if db.level() == DiagnosticLevel::Fatal { return self.handle_non_ignoreable_error(db); } if let Some(primary_span) = &db.span.primary_span() { @@ -292,7 +292,7 @@ mod tests { use super::*; use crate::config::IgnoreList; use crate::utils::mk_sp; - use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName, DUMMY_SP}; + use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName}; use std::path::PathBuf; use std::sync::atomic::AtomicU32; @@ -310,16 +310,12 @@ mod tests { } fn build_diagnostic(level: DiagnosticLevel, span: Option) -> Diagnostic { - Diagnostic { - level, - code: None, - message: vec![], - children: vec![], - suggestions: Ok(vec![]), - span: span.unwrap_or_else(MultiSpan::new), - sort_span: DUMMY_SP, - is_lint: false, + let mut diag = Diagnostic::new(level, ""); + diag.message.clear(); + if let Some(span) = span { + diag.span = span; } + diag } fn build_emitter( From 57239460813806d74f6e5404876a6f3fc8f9262b Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Wed, 26 Jan 2022 03:39:14 +0000 Subject: [PATCH 33/64] rustc_errors: take `self` by value in `DiagnosticBuilder::cancel`. --- src/modules.rs | 2 +- src/parse/macros/cfg_if.rs | 2 +- src/parse/macros/lazy_static.rs | 2 +- src/parse/macros/mod.rs | 2 +- src/parse/parser.rs | 2 +- src/parse/session.rs | 11 ----------- 6 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/modules.rs b/src/modules.rs index 9c964b274e088..d4bddd957858f 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -439,7 +439,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { } } Err(mod_err) if !mods_outside_ast.is_empty() => { - if let ModError::ParserError(mut e) = mod_err { + if let ModError::ParserError(e) = mod_err { e.cancel(); } Ok(Some(SubModKind::MultiExternal(mods_outside_ast))) diff --git a/src/parse/macros/cfg_if.rs b/src/parse/macros/cfg_if.rs index e10fbe64bcdbe..306b6bb745ee6 100644 --- a/src/parse/macros/cfg_if.rs +++ b/src/parse/macros/cfg_if.rs @@ -57,7 +57,7 @@ fn parse_cfg_if_inner<'a>( let item = match parser.parse_item(ForceCollect::No) { Ok(Some(item_ptr)) => item_ptr.into_inner(), Ok(None) => continue, - Err(mut err) => { + Err(err) => { err.cancel(); parser.sess.span_diagnostic.reset_err_count(); return Err( diff --git a/src/parse/macros/lazy_static.rs b/src/parse/macros/lazy_static.rs index 9c8651aa3faf7..4c541de04be08 100644 --- a/src/parse/macros/lazy_static.rs +++ b/src/parse/macros/lazy_static.rs @@ -23,7 +23,7 @@ pub(crate) fn parse_lazy_static( val } } - Err(mut err) => { + Err(err) => { err.cancel(); parser.sess.span_diagnostic.reset_err_count(); return None; diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs index 2e9ce1d35f400..fd738908170f8 100644 --- a/src/parse/macros/mod.rs +++ b/src/parse/macros/mod.rs @@ -36,7 +36,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { return Some(MacroArg::$macro_arg($f(x)?)); } } - Err(mut e) => { + Err(e) => { e.cancel(); parser.sess.span_diagnostic.reset_err_count(); } diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 657217633f4ab..f0944a88d2f22 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -115,7 +115,7 @@ impl<'a> Parser<'a> { match parser.parse_mod(&TokenKind::Eof) { Ok(result) => Some(result), Err(mut e) => { - sess.emit_or_cancel_diagnostic(&mut e); + e.emit(); if sess.can_reset_errors() { sess.reset_errors(); } diff --git a/src/parse/session.rs b/src/parse/session.rs index d26bb8c20255b..40a6d708d8ccc 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -230,17 +230,6 @@ impl ParseSess { } } - pub(crate) fn emit_or_cancel_diagnostic(&self, diagnostic: &mut Diagnostic) { - self.parse_sess.span_diagnostic.emit_diagnostic(diagnostic); - // The Handler will check whether the diagnostic should be emitted - // based on the user's rustfmt configuration and the originating file - // that caused the parser error. If the Handler determined it should skip - // emission then we need to ensure the diagnostic is cancelled. - if !diagnostic.cancelled() { - diagnostic.cancel(); - } - } - pub(super) fn can_reset_errors(&self) -> bool { self.can_reset_errors.load(Ordering::Acquire) } From 89ca3f3a100456d652b156b1a62b8e244e526c4e Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 23 Feb 2022 21:37:42 -0600 Subject: [PATCH 34/64] fix: unused test imports on non-nightly, prevent regression --- .github/workflows/linux.yml | 4 ++++ src/ignore_path.rs | 9 ++++----- src/lib.rs | 1 - 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index db49794164212..45f63b83c0562 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -40,6 +40,10 @@ jobs: rustc -Vv cargo -V cargo build + env: + RUSTFLAGS: '-D warnings' - name: test run: cargo test + env: + RUSTFLAGS: '-D warnings' diff --git a/src/ignore_path.rs b/src/ignore_path.rs index 7738eee0a7604..d955949496a67 100644 --- a/src/ignore_path.rs +++ b/src/ignore_path.rs @@ -32,16 +32,15 @@ impl IgnorePathSet { #[cfg(test)] mod test { - use std::path::{Path, PathBuf}; - - use crate::config::{Config, FileName}; - use crate::ignore_path::IgnorePathSet; - use rustfmt_config_proc_macro::nightly_only_test; #[nightly_only_test] #[test] fn test_ignore_path_set() { + use crate::config::{Config, FileName}; + use crate::ignore_path::IgnorePathSet; + use std::path::{Path, PathBuf}; + let config = Config::from_toml(r#"ignore = ["foo.rs", "bar_dir/*"]"#, Path::new("")).unwrap(); let ignore_path_set = IgnorePathSet::from_ignore_list(&config.ignore()).unwrap(); diff --git a/src/lib.rs b/src/lib.rs index fae8080c02e41..ad23b16e02ec1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ #![warn(unreachable_pub)] #![recursion_limit = "256"] #![allow(clippy::match_like_matches_macro)] -#![allow(unreachable_pub)] #[macro_use] extern crate derive_new; From de1ac375f079a9bf7f2aa81c47fa46ecdb32d074 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Wed, 23 Feb 2022 08:11:17 -0500 Subject: [PATCH 35/64] Enable rustc_pass_by_value for Span --- src/expr.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index e1865c8afc2f0..4f333cd27cefe 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1533,7 +1533,7 @@ fn rewrite_struct_lit<'a>( enum StructLitField<'a> { Regular(&'a ast::ExprField), Base(&'a ast::Expr), - Rest(&'a Span), + Rest(Span), } // 2 = " {".len() @@ -1568,7 +1568,7 @@ fn rewrite_struct_lit<'a>( let field_iter = fields.iter().map(StructLitField::Regular).chain( match struct_rest { ast::StructRest::Base(expr) => Some(StructLitField::Base(&**expr)), - ast::StructRest::Rest(span) => Some(StructLitField::Rest(span)), + ast::StructRest::Rest(span) => Some(StructLitField::Rest(*span)), ast::StructRest::None => None, } .into_iter(), From 12048e444f8a68b3c13e98b15c11a69de9f0b485 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 30 Jan 2022 13:46:53 -0500 Subject: [PATCH 36/64] fallback to dir_path when relative external mod resolution fails We only want to fall back if two conditions are met: 1) Initial module resolution is performed relative to some nested directory. 2) Module resolution fails because of a ModError::FileNotFound error. When these conditions are met we can try to fallback to searching for the module's file relative to the dir_path instead of the nested relative directory. Fixes 5198 As demonstrated by 5198, it's possible that a directory name conflicts with a rust file name. For example, src/lib/ and src/lib.rs. If src/lib.rs references an external module like ``mod foo;``, then module resolution will try to resolve ``foo`` to src/lib/foo.rs or src/lib/foo/mod.rs. Module resolution would fail with a file not found error if the ``foo`` module were defined at src/foo.rs. When encountering these kinds of module resolution issues we now fall back to the current directory and attempt to resolve the module again. Given the current example, this means that if we can't find the module ``foo`` at src/lib/foo.rs or src/lib/foo/mod.rs, we'll attempt to resolve the module to src/foo.rs. --- src/parse/session.rs | 22 +++++++++++++++++-- src/test/mod_resolver.rs | 16 ++++++++++++++ tests/mod-resolver/issue-5198/a.rs | 1 + tests/mod-resolver/issue-5198/lib.rs | 3 +++ tests/mod-resolver/issue-5198/lib/b.rs | 1 + tests/mod-resolver/issue-5198/lib/c/d.rs | 3 +++ .../issue-5198/lib/c/d/explanation.txt | 16 ++++++++++++++ tests/mod-resolver/issue-5198/lib/c/d/f.rs | 1 + .../mod-resolver/issue-5198/lib/c/d/g/mod.rs | 1 + tests/mod-resolver/issue-5198/lib/c/e.rs | 1 + tests/mod-resolver/issue-5198/lib/c/mod.rs | 3 +++ .../issue-5198/lib/explanation.txt | 16 ++++++++++++++ 12 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 tests/mod-resolver/issue-5198/a.rs create mode 100644 tests/mod-resolver/issue-5198/lib.rs create mode 100644 tests/mod-resolver/issue-5198/lib/b.rs create mode 100644 tests/mod-resolver/issue-5198/lib/c/d.rs create mode 100644 tests/mod-resolver/issue-5198/lib/c/d/explanation.txt create mode 100644 tests/mod-resolver/issue-5198/lib/c/d/f.rs create mode 100644 tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs create mode 100644 tests/mod-resolver/issue-5198/lib/c/e.rs create mode 100644 tests/mod-resolver/issue-5198/lib/c/mod.rs create mode 100644 tests/mod-resolver/issue-5198/lib/explanation.txt diff --git a/src/parse/session.rs b/src/parse/session.rs index 624fed0d2de26..fb9182152d1da 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -12,6 +12,7 @@ use rustc_span::{ use crate::config::file_lines::LineRange; use crate::ignore_path::IgnorePathSet; +use crate::parse::parser::{ModError, ModulePathSuccess}; use crate::source_map::LineRangeUtils; use crate::utils::starts_with_newline; use crate::visitor::SnippetProvider; @@ -145,13 +146,30 @@ impl ParseSess { }) } + /// Determine the submodule path for the given module identifier. + /// + /// * `id` - The name of the module + /// * `relative` - If Some(symbol), the symbol name is a directory relative to the dir_path. + /// If relative is Some, resolve the submodle at {dir_path}/{symbol}/{id}.rs + /// or {dir_path}/{symbol}/{id}/mod.rs. if None, resolve the module at {dir_path}/{id}.rs. + /// * `dir_path` - Module resolution will occur relative to this direcotry. pub(crate) fn default_submod_path( &self, id: symbol::Ident, relative: Option, dir_path: &Path, - ) -> Result> { - rustc_expand::module::default_submod_path(&self.parse_sess, id, relative, dir_path) + ) -> Result> { + rustc_expand::module::default_submod_path(&self.parse_sess, id, relative, dir_path).or_else( + |e| { + // If resloving a module relative to {dir_path}/{symbol} fails because a file + // could not be found, then try to resolve the module relative to {dir_path}. + if matches!(e, ModError::FileNotFound(..)) && relative.is_some() { + rustc_expand::module::default_submod_path(&self.parse_sess, id, None, dir_path) + } else { + Err(e) + } + }, + ) } pub(crate) fn is_file_parsed(&self, path: &Path) -> bool { diff --git a/src/test/mod_resolver.rs b/src/test/mod_resolver.rs index fcff6d14e6fa4..aacb2acc68498 100644 --- a/src/test/mod_resolver.rs +++ b/src/test/mod_resolver.rs @@ -64,3 +64,19 @@ fn fmt_out_of_line_test_modules() { ], ) } + +#[test] +fn fallback_and_try_to_resolve_external_submod_relative_to_current_dir_path() { + // See also https://github.com/rust-lang/rustfmt/issues/5198 + verify_mod_resolution( + "tests/mod-resolver/issue-5198/lib.rs", + &[ + "tests/mod-resolver/issue-5198/a.rs", + "tests/mod-resolver/issue-5198/lib/b.rs", + "tests/mod-resolver/issue-5198/lib/c/mod.rs", + "tests/mod-resolver/issue-5198/lib/c/e.rs", + "tests/mod-resolver/issue-5198/lib/c/d/f.rs", + "tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs", + ], + ) +} diff --git a/tests/mod-resolver/issue-5198/a.rs b/tests/mod-resolver/issue-5198/a.rs new file mode 100644 index 0000000000000..cd686f5611690 --- /dev/null +++ b/tests/mod-resolver/issue-5198/a.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib.rs b/tests/mod-resolver/issue-5198/lib.rs new file mode 100644 index 0000000000000..696832913c879 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib.rs @@ -0,0 +1,3 @@ +mod a; +mod b; +mod c; diff --git a/tests/mod-resolver/issue-5198/lib/b.rs b/tests/mod-resolver/issue-5198/lib/b.rs new file mode 100644 index 0000000000000..cd686f5611690 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/b.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib/c/d.rs b/tests/mod-resolver/issue-5198/lib/c/d.rs new file mode 100644 index 0000000000000..d1604aa23a3cb --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/d.rs @@ -0,0 +1,3 @@ +mod e; +mod f; +mod g; diff --git a/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt b/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt new file mode 100644 index 0000000000000..92c9e3021431f --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt @@ -0,0 +1,16 @@ +This file is contained in the './lib/c/d/' directory. + +The directory name './lib/c/d/' conflicts with the './lib/c/d.rs' file name. + +'./lib/c/d.rs' defines 3 external modules: + + * mod e; + * mod f; + * mod g; + +Module resolution will fail if we look for './lib/c/d/e.rs' or './lib/c/d/e/mod.rs', +so we should fall back to looking for './lib/c/e.rs', which correctly finds the modlue, that +rustfmt should format. + +'./lib/c/d/f.rs' and './lib/c/d/g/mod.rs' exist at the default submodule paths so we should be able +to resolve these modules with no problems. \ No newline at end of file diff --git a/tests/mod-resolver/issue-5198/lib/c/d/f.rs b/tests/mod-resolver/issue-5198/lib/c/d/f.rs new file mode 100644 index 0000000000000..cd686f5611690 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/d/f.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs b/tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs new file mode 100644 index 0000000000000..cd686f5611690 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib/c/e.rs b/tests/mod-resolver/issue-5198/lib/c/e.rs new file mode 100644 index 0000000000000..cd686f5611690 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/e.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib/c/mod.rs b/tests/mod-resolver/issue-5198/lib/c/mod.rs new file mode 100644 index 0000000000000..81904619650f9 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/c/mod.rs @@ -0,0 +1,3 @@ +mod d; + +fn main( ) { println!("Hello World!") } diff --git a/tests/mod-resolver/issue-5198/lib/explanation.txt b/tests/mod-resolver/issue-5198/lib/explanation.txt new file mode 100644 index 0000000000000..d436a8076cd71 --- /dev/null +++ b/tests/mod-resolver/issue-5198/lib/explanation.txt @@ -0,0 +1,16 @@ +This file is contained in the './lib' directory. + +The directory name './lib' conflicts with the './lib.rs' file name. + +'lib.rs' defines 3 external modules: + + * mod a; + * mod b; + * mod c; + +Module resolution will fail if we look for './lib/a.rs' or './lib/a/mod.rs', +so we should fall back to looking for './a.rs', which correctly finds the modlue that +rustfmt should format. + +'./lib/b.rs' and './lib/c/mod.rs' exist at the default submodule paths so we should be able +to resolve these modules with no problems. From 272fb42f06479afb62a9503f181540101f67982a Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Fri, 25 Feb 2022 11:31:09 -0500 Subject: [PATCH 37/64] Prevent wrapping markdown headers in doc comments Fixes 5238 A markdown header is defined by a string that starts with `#`. Previously, rustfmt would wrap long markdown headers when `wrap_comments=true`. This lead to issues when rendering these headers in HTML using rustdoc. Now, rustfmt leaves markdown headers alone when wrapping comments. --- src/comment.rs | 18 ++++++++++++++---- .../markdown_header_wrap_comments_false.rs | 11 +++++++++++ .../markdown_header_wrap_comments_true.rs | 11 +++++++++++ .../markdown_header_wrap_comments_false.rs | 11 +++++++++++ .../markdown_header_wrap_comments_true.rs | 14 ++++++++++++++ 5 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 tests/source/issue-5238/markdown_header_wrap_comments_false.rs create mode 100644 tests/source/issue-5238/markdown_header_wrap_comments_true.rs create mode 100644 tests/target/issue-5238/markdown_header_wrap_comments_false.rs create mode 100644 tests/target/issue-5238/markdown_header_wrap_comments_true.rs diff --git a/src/comment.rs b/src/comment.rs index 96778c4ef1ddb..f9d8a0fa70c00 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -683,6 +683,7 @@ impl<'a> CommentRewrite<'a> { i: usize, line: &'a str, has_leading_whitespace: bool, + is_doc_comment: bool, ) -> bool { let num_newlines = count_newlines(orig); let is_last = i == num_newlines; @@ -789,10 +790,19 @@ impl<'a> CommentRewrite<'a> { } } - if self.fmt.config.wrap_comments() + let is_markdown_header_doc_comment = is_doc_comment && line.starts_with("#"); + + // We only want to wrap the comment if: + // 1) wrap_comments = true is configured + // 2) The comment is not the start of a markdown header doc comment + // 3) The comment width exceeds the shape's width + // 4) No URLS were found in the commnet + let should_wrap_comment = self.fmt.config.wrap_comments() + && !is_markdown_header_doc_comment && unicode_str_width(line) > self.fmt.shape.width - && !has_url(line) - { + && !has_url(line); + + if should_wrap_comment { match rewrite_string(line, &self.fmt, self.max_width) { Some(ref s) => { self.is_prev_line_multi_line = s.contains('\n'); @@ -882,7 +892,7 @@ fn rewrite_comment_inner( }); for (i, (line, has_leading_whitespace)) in lines.enumerate() { - if rewriter.handle_line(orig, i, line, has_leading_whitespace) { + if rewriter.handle_line(orig, i, line, has_leading_whitespace, is_doc_comment) { break; } } diff --git a/tests/source/issue-5238/markdown_header_wrap_comments_false.rs b/tests/source/issue-5238/markdown_header_wrap_comments_false.rs new file mode 100644 index 0000000000000..229c6e5753d2e --- /dev/null +++ b/tests/source/issue-5238/markdown_header_wrap_comments_false.rs @@ -0,0 +1,11 @@ +// rustfmt-wrap_comments: false + +/// no markdown header so rustfmt should wrap this comment when `format_code_in_doc_comments = true` and `wrap_comments = true` +fn not_documented_with_markdown_header() { + // This is just a normal inline comment so rustfmt should wrap this comment when `wrap_comments = true` +} + +/// # We're using a markdown header here so rustfmt should refuse to wrap this comment in all circumstances +fn documented_with_markdown_header() { + // # We're using a markdown header in an inline comment. rustfmt should be able to wrap this comment when `wrap_comments = true` +} diff --git a/tests/source/issue-5238/markdown_header_wrap_comments_true.rs b/tests/source/issue-5238/markdown_header_wrap_comments_true.rs new file mode 100644 index 0000000000000..c547ff35c691b --- /dev/null +++ b/tests/source/issue-5238/markdown_header_wrap_comments_true.rs @@ -0,0 +1,11 @@ +// rustfmt-wrap_comments: true + +/// no markdown header so rustfmt should wrap this comment when `format_code_in_doc_comments = true` and `wrap_comments = true` +fn not_documented_with_markdown_header() { + // This is just a normal inline comment so rustfmt should wrap this comment when `wrap_comments = true` +} + +/// # We're using a markdown header here so rustfmt should refuse to wrap this comment in all circumstances +fn documented_with_markdown_header() { + // # We're using a markdown header in an inline comment. rustfmt should be able to wrap this comment when `wrap_comments = true` +} diff --git a/tests/target/issue-5238/markdown_header_wrap_comments_false.rs b/tests/target/issue-5238/markdown_header_wrap_comments_false.rs new file mode 100644 index 0000000000000..229c6e5753d2e --- /dev/null +++ b/tests/target/issue-5238/markdown_header_wrap_comments_false.rs @@ -0,0 +1,11 @@ +// rustfmt-wrap_comments: false + +/// no markdown header so rustfmt should wrap this comment when `format_code_in_doc_comments = true` and `wrap_comments = true` +fn not_documented_with_markdown_header() { + // This is just a normal inline comment so rustfmt should wrap this comment when `wrap_comments = true` +} + +/// # We're using a markdown header here so rustfmt should refuse to wrap this comment in all circumstances +fn documented_with_markdown_header() { + // # We're using a markdown header in an inline comment. rustfmt should be able to wrap this comment when `wrap_comments = true` +} diff --git a/tests/target/issue-5238/markdown_header_wrap_comments_true.rs b/tests/target/issue-5238/markdown_header_wrap_comments_true.rs new file mode 100644 index 0000000000000..87dae58eccd73 --- /dev/null +++ b/tests/target/issue-5238/markdown_header_wrap_comments_true.rs @@ -0,0 +1,14 @@ +// rustfmt-wrap_comments: true + +/// no markdown header so rustfmt should wrap this comment when +/// `format_code_in_doc_comments = true` and `wrap_comments = true` +fn not_documented_with_markdown_header() { + // This is just a normal inline comment so rustfmt should wrap this comment + // when `wrap_comments = true` +} + +/// # We're using a markdown header here so rustfmt should refuse to wrap this comment in all circumstances +fn documented_with_markdown_header() { + // # We're using a markdown header in an inline comment. rustfmt should be + // able to wrap this comment when `wrap_comments = true` +} From 4edb7578261ae0cc547c4346f8c0a82e124ac524 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 20 Jan 2022 11:06:45 -0500 Subject: [PATCH 38/64] refactor: prepare to associate multiple spans with a module. --- src/parse/parser.rs | 2 +- src/visitor.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parse/parser.rs b/src/parse/parser.rs index f0944a88d2f22..3b4e762b6dd15 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -113,7 +113,7 @@ impl<'a> Parser<'a> { let result = catch_unwind(AssertUnwindSafe(|| { let mut parser = new_parser_from_file(sess.inner(), path, Some(span)); match parser.parse_mod(&TokenKind::Eof) { - Ok(result) => Some(result), + Ok((a, i, ast::ModSpans { inner_span })) => Some((a, i, inner_span)), Err(mut e) => { e.emit(); if sess.can_reset_errors() { diff --git a/src/visitor.rs b/src/visitor.rs index 0177689958aa7..57a58c6048466 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -915,7 +915,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { let ident_str = rewrite_ident(&self.get_context(), ident).to_owned(); self.push_str(&ident_str); - if let ast::ModKind::Loaded(ref items, ast::Inline::Yes, inner_span) = mod_kind { + if let ast::ModKind::Loaded(ref items, ast::Inline::Yes, ast::ModSpans{ inner_span }) = mod_kind { match self.config.brace_style() { BraceStyle::AlwaysNextLine => { let indent_str = self.block_indent.to_string_with_newline(self.config); From 74876ef4e9b29184787f6d8f3ba447e78def3a47 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 3 Mar 2022 18:45:25 -0500 Subject: [PATCH 39/64] Associate multiple with a crate too. --- src/modules.rs | 4 ++-- src/visitor.rs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modules.rs b/src/modules.rs index d4bddd957858f..64d96a5c6a6e4 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -124,7 +124,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { mut self, krate: &'ast ast::Crate, ) -> Result, ModuleResolutionError> { - let root_filename = self.parse_sess.span_to_filename(krate.span); + let root_filename = self.parse_sess.span_to_filename(krate.spans.inner_span); self.directory.path = match root_filename { FileName::Real(ref p) => p.parent().unwrap_or(Path::new("")).to_path_buf(), _ => PathBuf::new(), @@ -135,7 +135,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { self.visit_mod_from_ast(&krate.items)?; } - let snippet_provider = self.parse_sess.snippet_provider(krate.span); + let snippet_provider = self.parse_sess.snippet_provider(krate.spans.inner_span); self.file_map.insert( root_filename, diff --git a/src/visitor.rs b/src/visitor.rs index 57a58c6048466..c44b2fc6ae354 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -915,7 +915,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { let ident_str = rewrite_ident(&self.get_context(), ident).to_owned(); self.push_str(&ident_str); - if let ast::ModKind::Loaded(ref items, ast::Inline::Yes, ast::ModSpans{ inner_span }) = mod_kind { + if let ast::ModKind::Loaded(ref items, ast::Inline::Yes, ref spans) = mod_kind { + let ast::ModSpans { inner_span } = *spans; match self.config.brace_style() { BraceStyle::AlwaysNextLine => { let indent_str = self.block_indent.to_string_with_newline(self.config); From 651f46376aa6bb259c58cbb3debad6e0edce31bf Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 20 Jan 2022 14:07:54 -0500 Subject: [PATCH 40/64] Adjusted diagnostic output so that if there is no `use` in a item sequence, then we just suggest the first legal position where you could inject a use. To do this, I added `inject_use_span` field to `ModSpans`, and populate it in parser (it is the span of the first token found after inner attributes, if any). Then I rewrote the use-suggestion code to utilize it, and threw out some stuff that is now unnecessary with this in place. (I think the result is easier to understand.) Then I added a test of issue 87613. --- src/parse/parser.rs | 2 +- src/visitor.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 3b4e762b6dd15..6983249c15d45 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -113,7 +113,7 @@ impl<'a> Parser<'a> { let result = catch_unwind(AssertUnwindSafe(|| { let mut parser = new_parser_from_file(sess.inner(), path, Some(span)); match parser.parse_mod(&TokenKind::Eof) { - Ok((a, i, ast::ModSpans { inner_span })) => Some((a, i, inner_span)), + Ok((a, i, ast::ModSpans { inner_span, inject_use_span: _ })) => Some((a, i, inner_span)), Err(mut e) => { e.emit(); if sess.can_reset_errors() { diff --git a/src/visitor.rs b/src/visitor.rs index c44b2fc6ae354..dec977e98caf5 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -916,7 +916,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { self.push_str(&ident_str); if let ast::ModKind::Loaded(ref items, ast::Inline::Yes, ref spans) = mod_kind { - let ast::ModSpans { inner_span } = *spans; + let ast::ModSpans{ inner_span, inject_use_span: _ } = *spans; match self.config.brace_style() { BraceStyle::AlwaysNextLine => { let indent_str = self.block_indent.to_string_with_newline(self.config); From 0be893166b2ae94db8a10ea16c3d9465e378c27a Mon Sep 17 00:00:00 2001 From: pierwill Date: Fri, 4 Mar 2022 11:54:28 -0600 Subject: [PATCH 41/64] Update `itertools` Update to 0.10.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8d9c4a7fb20cd..764714638a978 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ rustfmt-format-diff = [] generic-simd = ["bytecount/generic-simd"] [dependencies] -itertools = "0.9" +itertools = "0.10.1" toml = "0.5" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" From ce301d92f12658edf6fe410d39de445270d1420e Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 4 Mar 2022 17:05:30 -0500 Subject: [PATCH 42/64] Placate tidy in submodule. --- src/parse/parser.rs | 8 +++++++- src/visitor.rs | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 6983249c15d45..ec051d9371017 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -113,7 +113,13 @@ impl<'a> Parser<'a> { let result = catch_unwind(AssertUnwindSafe(|| { let mut parser = new_parser_from_file(sess.inner(), path, Some(span)); match parser.parse_mod(&TokenKind::Eof) { - Ok((a, i, ast::ModSpans { inner_span, inject_use_span: _ })) => Some((a, i, inner_span)), + Ok((a, + i, + ast::ModSpans { + inner_span, + inject_use_span: _ + } + )) => Some((a, i, inner_span)), Err(mut e) => { e.emit(); if sess.can_reset_errors() { diff --git a/src/visitor.rs b/src/visitor.rs index dec977e98caf5..dcf096294f14f 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -916,7 +916,10 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { self.push_str(&ident_str); if let ast::ModKind::Loaded(ref items, ast::Inline::Yes, ref spans) = mod_kind { - let ast::ModSpans{ inner_span, inject_use_span: _ } = *spans; + let ast::ModSpans{ + inner_span, + inject_use_span: _ + } = *spans; match self.config.brace_style() { BraceStyle::AlwaysNextLine => { let indent_str = self.block_indent.to_string_with_newline(self.config); From 1212c9477c26335487ff1094a3d7d454c16365b5 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Tue, 19 Oct 2021 18:45:48 -0400 Subject: [PATCH 43/64] Change syntax for TyAlias where clauses --- src/items.rs | 108 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 34 deletions(-) diff --git a/src/items.rs b/src/items.rs index babc56f86edc6..8498cb6addaa1 100644 --- a/src/items.rs +++ b/src/items.rs @@ -694,7 +694,8 @@ pub(crate) fn format_impl( let where_span_end = context.snippet_provider.opt_span_before(missing_span, "{"); let where_clause_str = rewrite_where_clause( context, - &generics.where_clause, + &generics.where_clause.predicates, + generics.where_clause.span, context.config.brace_style(), Shape::legacy(where_budget, offset.block_only()), false, @@ -1059,7 +1060,8 @@ pub(crate) fn format_trait( let option = WhereClauseOption::snuggled(&generics_str); let where_clause_str = rewrite_where_clause( context, - &generics.where_clause, + &generics.where_clause.predicates, + generics.where_clause.span, context.config.brace_style(), Shape::legacy(where_budget, offset.block_only()), where_on_new_line, @@ -1178,7 +1180,8 @@ impl<'a> Rewrite for TraitAliasBounds<'a> { let where_str = rewrite_where_clause( context, - &self.generics.where_clause, + &self.generics.where_clause.predicates, + self.generics.where_clause.span, context.config.brace_style(), shape, false, @@ -1437,7 +1440,8 @@ fn format_tuple_struct( let option = WhereClauseOption::new(true, WhereClauseSpace::Newline); rewrite_where_clause( context, - &generics.where_clause, + &generics.where_clause.predicates, + generics.where_clause.span, context.config.brace_style(), Shape::legacy(where_budget, offset.block_only()), false, @@ -1503,6 +1507,8 @@ struct TyAliasRewriteInfo<'c, 'g>( &'c RewriteContext<'c>, Indent, &'g ast::Generics, + (ast::TyAliasWhereClause, ast::TyAliasWhereClause), + usize, symbol::Ident, Span, ); @@ -1521,6 +1527,8 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( ref generics, ref bounds, ref ty, + where_clauses, + where_predicates_split, } = *ty_alias_kind; let ty_opt = ty.as_ref(); let (ident, vis) = match visitor_kind { @@ -1528,7 +1536,15 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( AssocTraitItem(i) | AssocImplItem(i) => (i.ident, &i.vis), ForeignItem(i) => (i.ident, &i.vis), }; - let rw_info = &TyAliasRewriteInfo(context, indent, generics, ident, span); + let rw_info = &TyAliasRewriteInfo( + context, + indent, + generics, + where_clauses, + where_predicates_split, + ident, + span, + ); let op_ty = opaque_ty(ty); // Type Aliases are formatted slightly differently depending on the context // in which they appear, whether they are opaque, and whether they are associated. @@ -1564,7 +1580,22 @@ fn rewrite_ty( vis: &ast::Visibility, ) -> Option { let mut result = String::with_capacity(128); - let TyAliasRewriteInfo(context, indent, generics, ident, span) = *rw_info; + let TyAliasRewriteInfo( + context, + indent, + generics, + where_clauses, + where_predicates_split, + ident, + span, + ) = *rw_info; + let (before_where_predicates, after_where_predicates) = generics + .where_clause + .predicates + .split_at(where_predicates_split); + if !after_where_predicates.is_empty() { + return None; + } result.push_str(&format!("{}type ", format_visibility(context, vis))); let ident_str = rewrite_ident(context, ident); @@ -1595,7 +1626,8 @@ fn rewrite_ty( } let where_clause_str = rewrite_where_clause( context, - &generics.where_clause, + before_where_predicates, + where_clauses.0.1, context.config.brace_style(), Shape::legacy(where_budget, indent), false, @@ -1609,7 +1641,7 @@ fn rewrite_ty( if let Some(ty) = rhs { // If there's a where clause, add a newline before the assignment. Otherwise just add a // space. - let has_where = !generics.where_clause.predicates.is_empty(); + let has_where = !before_where_predicates.is_empty(); if has_where { result.push_str(&indent.to_string_with_newline(context.config)); } else { @@ -1619,7 +1651,7 @@ fn rewrite_ty( let comment_span = context .snippet_provider .opt_span_before(span, "=") - .map(|op_lo| mk_sp(generics.where_clause.span.hi(), op_lo)); + .map(|op_lo| mk_sp(where_clauses.0.1.hi(), op_lo)); let lhs = match comment_span { Some(comment_span) @@ -2176,7 +2208,7 @@ fn rewrite_fn_base( let generics_str = rewrite_generics( context, rewrite_ident(context, ident), - fn_sig.generics, + &fn_sig.generics, shape, )?; result.push_str(&generics_str); @@ -2416,7 +2448,8 @@ fn rewrite_fn_base( } let where_clause_str = rewrite_where_clause( context, - where_clause, + &where_clause.predicates, + where_clause.span, context.config.brace_style(), Shape::indented(indent, context.config), true, @@ -2692,7 +2725,8 @@ fn generics_shape_from_config(config: &Config, shape: Shape, offset: usize) -> O fn rewrite_where_clause_rfc_style( context: &RewriteContext<'_>, - where_clause: &ast::WhereClause, + predicates: &[ast::WherePredicate], + where_span: Span, shape: Shape, terminator: &str, span_end: Option, @@ -2701,7 +2735,8 @@ fn rewrite_where_clause_rfc_style( ) -> Option { let (where_keyword, allow_single_line) = rewrite_where_keyword( context, - where_clause, + predicates, + where_span, shape, span_end_before_where, where_clause_option, @@ -2714,12 +2749,12 @@ fn rewrite_where_clause_rfc_style( .block_left(context.config.tab_spaces())? .sub_width(1)?; let force_single_line = context.config.where_single_line() - && where_clause.predicates.len() == 1 + && predicates.len() == 1 && !where_clause_option.veto_single_line; let preds_str = rewrite_bounds_on_where_clause( context, - where_clause, + predicates, clause_shape, terminator, span_end, @@ -2743,7 +2778,8 @@ fn rewrite_where_clause_rfc_style( /// Rewrite `where` and comment around it. fn rewrite_where_keyword( context: &RewriteContext<'_>, - where_clause: &ast::WhereClause, + predicates: &[ast::WherePredicate], + where_span: Span, shape: Shape, span_end_before_where: BytePos, where_clause_option: WhereClauseOption, @@ -2763,7 +2799,7 @@ fn rewrite_where_keyword( }; let (span_before, span_after) = - missing_span_before_after_where(span_end_before_where, where_clause); + missing_span_before_after_where(span_end_before_where, predicates, where_span); let (comment_before, comment_after) = rewrite_comments_before_after_where(context, span_before, span_after, shape)?; @@ -2789,22 +2825,22 @@ fn rewrite_where_keyword( /// Rewrite bounds on a where clause. fn rewrite_bounds_on_where_clause( context: &RewriteContext<'_>, - where_clause: &ast::WhereClause, + predicates: &[ast::WherePredicate], shape: Shape, terminator: &str, span_end: Option, where_clause_option: WhereClauseOption, force_single_line: bool, ) -> Option { - let span_start = where_clause.predicates[0].span().lo(); + let span_start = predicates[0].span().lo(); // If we don't have the start of the next span, then use the end of the // predicates, but that means we miss comments. - let len = where_clause.predicates.len(); - let end_of_preds = where_clause.predicates[len - 1].span().hi(); + let len = predicates.len(); + let end_of_preds = predicates[len - 1].span().hi(); let span_end = span_end.unwrap_or(end_of_preds); let items = itemize_list( context.snippet_provider, - where_clause.predicates.iter(), + predicates.iter(), terminator, ",", |pred| pred.span().lo(), @@ -2837,7 +2873,8 @@ fn rewrite_bounds_on_where_clause( fn rewrite_where_clause( context: &RewriteContext<'_>, - where_clause: &ast::WhereClause, + predicates: &[ast::WherePredicate], + where_span: Span, brace_style: BraceStyle, shape: Shape, on_new_line: bool, @@ -2846,14 +2883,15 @@ fn rewrite_where_clause( span_end_before_where: BytePos, where_clause_option: WhereClauseOption, ) -> Option { - if where_clause.predicates.is_empty() { + if predicates.is_empty() { return Some(String::new()); } if context.config.indent_style() == IndentStyle::Block { return rewrite_where_clause_rfc_style( context, - where_clause, + predicates, + where_span, shape, terminator, span_end, @@ -2873,15 +2911,15 @@ fn rewrite_where_clause( // be out by a char or two. let budget = context.config.max_width() - offset.width(); - let span_start = where_clause.predicates[0].span().lo(); + let span_start = predicates[0].span().lo(); // If we don't have the start of the next span, then use the end of the // predicates, but that means we miss comments. - let len = where_clause.predicates.len(); - let end_of_preds = where_clause.predicates[len - 1].span().hi(); + let len = predicates.len(); + let end_of_preds = predicates[len - 1].span().hi(); let span_end = span_end.unwrap_or(end_of_preds); let items = itemize_list( context.snippet_provider, - where_clause.predicates.iter(), + predicates.iter(), terminator, ",", |pred| pred.span().lo(), @@ -2936,12 +2974,13 @@ fn rewrite_where_clause( fn missing_span_before_after_where( before_item_span_end: BytePos, - where_clause: &ast::WhereClause, + predicates: &[ast::WherePredicate], + where_span: Span, ) -> (Span, Span) { - let missing_span_before = mk_sp(before_item_span_end, where_clause.span.lo()); + let missing_span_before = mk_sp(before_item_span_end, where_span.lo()); // 5 = `where` - let pos_after_where = where_clause.span.lo() + BytePos(5); - let missing_span_after = mk_sp(pos_after_where, where_clause.predicates[0].span().lo()); + let pos_after_where = where_span.lo() + BytePos(5); + let missing_span_after = mk_sp(pos_after_where, predicates[0].span().lo()); (missing_span_before, missing_span_after) } @@ -3030,7 +3069,8 @@ fn format_generics( } let where_clause_str = rewrite_where_clause( context, - &generics.where_clause, + &generics.where_clause.predicates, + generics.where_clause.span, brace_style, Shape::legacy(budget, offset.block_only()), true, From ee130515e3788d886cdf18442871936a99c134b6 Mon Sep 17 00:00:00 2001 From: cassaundra Date: Wed, 29 Dec 2021 15:41:26 -0800 Subject: [PATCH 44/64] Fix missing struct field separators under certain conditions When struct_field_align_threshold is non-zero and trailing_comma is set to "Never," struct field separators are omitted between field groups. This issue is resolved by forcing separators between groups. Fixes #4791. A test is included with a minimal reproducible example. --- src/vertical.rs | 23 ++++++++++++++++++++--- tests/source/issue_4791.rs | 14 ++++++++++++++ tests/target/issue_4791.rs | 14 ++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 tests/source/issue_4791.rs create mode 100644 tests/target/issue_4791.rs diff --git a/src/vertical.rs b/src/vertical.rs index c4208848c6c2a..a06bc995aa55e 100644 --- a/src/vertical.rs +++ b/src/vertical.rs @@ -160,8 +160,18 @@ pub(crate) fn rewrite_with_alignment( }; let init_span = mk_sp(span.lo(), init_last_pos); let one_line_width = if rest.is_empty() { one_line_width } else { 0 }; - let result = - rewrite_aligned_items_inner(context, init, init_span, shape.indent, one_line_width)?; + + // if another group follows, we must force a separator + let force_separator = !rest.is_empty(); + + let result = rewrite_aligned_items_inner( + context, + init, + init_span, + shape.indent, + one_line_width, + force_separator, + )?; if rest.is_empty() { Some(result + spaces) } else { @@ -201,6 +211,7 @@ fn rewrite_aligned_items_inner( span: Span, offset: Indent, one_line_width: usize, + force_trailing_separator: bool, ) -> Option { // 1 = "," let item_shape = Shape::indented(offset, context.config).sub_width(1)?; @@ -246,9 +257,15 @@ fn rewrite_aligned_items_inner( }); } + let separator_tactic = if force_trailing_separator { + SeparatorTactic::Always + } else { + context.config.trailing_comma() + }; + let fmt = ListFormatting::new(item_shape, context.config) .tactic(tactic) - .trailing_separator(context.config.trailing_comma()) + .trailing_separator(separator_tactic) .preserve_newline(true); write_list(&items, &fmt) } diff --git a/tests/source/issue_4791.rs b/tests/source/issue_4791.rs new file mode 100644 index 0000000000000..4760022eeaf0d --- /dev/null +++ b/tests/source/issue_4791.rs @@ -0,0 +1,14 @@ +// rustfmt-struct_field_align_threshold: 30 +// rustfmt-trailing_comma: Never + +struct Foo { + group_a: u8, + + group_b: u8, +} + +struct Bar { + group_a: u8, + + group_b: u8 +} diff --git a/tests/target/issue_4791.rs b/tests/target/issue_4791.rs new file mode 100644 index 0000000000000..fff58be99a509 --- /dev/null +++ b/tests/target/issue_4791.rs @@ -0,0 +1,14 @@ +// rustfmt-struct_field_align_threshold: 30 +// rustfmt-trailing_comma: Never + +struct Foo { + group_a: u8, + + group_b: u8 +} + +struct Bar { + group_a: u8, + + group_b: u8 +} From ab9f2a8ac78a66ae1fb0f92ae5a32ce5c0db0d8d Mon Sep 17 00:00:00 2001 From: cassaundra Date: Fri, 14 Jan 2022 17:25:46 -0800 Subject: [PATCH 45/64] Add more tests for struct_field_align_threshold and trailing_comma --- .../source/{issue_4791.rs => issue-4791/buggy.rs} | 0 tests/source/issue-4791/trailing_comma.rs | 14 ++++++++++++++ .../target/{issue_4791.rs => issue-4791/buggy.rs} | 0 tests/target/issue-4791/no_trailing_comma.rs | 8 ++++++++ tests/target/issue-4791/trailing_comma.rs | 14 ++++++++++++++ 5 files changed, 36 insertions(+) rename tests/source/{issue_4791.rs => issue-4791/buggy.rs} (100%) create mode 100644 tests/source/issue-4791/trailing_comma.rs rename tests/target/{issue_4791.rs => issue-4791/buggy.rs} (100%) create mode 100644 tests/target/issue-4791/no_trailing_comma.rs create mode 100644 tests/target/issue-4791/trailing_comma.rs diff --git a/tests/source/issue_4791.rs b/tests/source/issue-4791/buggy.rs similarity index 100% rename from tests/source/issue_4791.rs rename to tests/source/issue-4791/buggy.rs diff --git a/tests/source/issue-4791/trailing_comma.rs b/tests/source/issue-4791/trailing_comma.rs new file mode 100644 index 0000000000000..c56c70faeae40 --- /dev/null +++ b/tests/source/issue-4791/trailing_comma.rs @@ -0,0 +1,14 @@ +// rustfmt-struct_field_align_threshold: 30 +// rustfmt-trailing_comma: Always + +struct Foo { + group_a: u8, + + group_b: u8 +} + +struct Bar { + group_a: u8, + + group_b: u8, +} diff --git a/tests/target/issue_4791.rs b/tests/target/issue-4791/buggy.rs similarity index 100% rename from tests/target/issue_4791.rs rename to tests/target/issue-4791/buggy.rs diff --git a/tests/target/issue-4791/no_trailing_comma.rs b/tests/target/issue-4791/no_trailing_comma.rs new file mode 100644 index 0000000000000..4a37163969ae9 --- /dev/null +++ b/tests/target/issue-4791/no_trailing_comma.rs @@ -0,0 +1,8 @@ +// rustfmt-struct_field_align_threshold: 0 +// rustfmt-trailing_comma: Never + +pub struct Baz { + group_a: u8, + + group_b: u8 +} diff --git a/tests/target/issue-4791/trailing_comma.rs b/tests/target/issue-4791/trailing_comma.rs new file mode 100644 index 0000000000000..29a224b3f6d96 --- /dev/null +++ b/tests/target/issue-4791/trailing_comma.rs @@ -0,0 +1,14 @@ +// rustfmt-struct_field_align_threshold: 30 +// rustfmt-trailing_comma: Always + +struct Foo { + group_a: u8, + + group_b: u8, +} + +struct Bar { + group_a: u8, + + group_b: u8, +} From 58de4142c525cc50824dab1cf199dc967c88356f Mon Sep 17 00:00:00 2001 From: cassaundra Date: Sat, 5 Mar 2022 14:17:15 -0800 Subject: [PATCH 46/64] Add test for issue #4791 --- tests/target/issue-4791/issue_4928.rs | 70 +++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 tests/target/issue-4791/issue_4928.rs diff --git a/tests/target/issue-4791/issue_4928.rs b/tests/target/issue-4791/issue_4928.rs new file mode 100644 index 0000000000000..588656b535fa1 --- /dev/null +++ b/tests/target/issue-4791/issue_4928.rs @@ -0,0 +1,70 @@ +// rustfmt-brace_style: SameLineWhere +// rustfmt-comment_width: 100 +// rustfmt-edition: 2018 +// rustfmt-fn_args_layout: Compressed +// rustfmt-hard_tabs: false +// rustfmt-match_block_trailing_comma: true +// rustfmt-max_width: 100 +// rustfmt-merge_derives: false +// rustfmt-newline_style: Unix +// rustfmt-normalize_doc_attributes: true +// rustfmt-overflow_delimited_expr: true +// rustfmt-reorder_imports: false +// rustfmt-reorder_modules: true +// rustfmt-struct_field_align_threshold: 20 +// rustfmt-tab_spaces: 4 +// rustfmt-trailing_comma: Never +// rustfmt-use_small_heuristics: Max +// rustfmt-use_try_shorthand: true +// rustfmt-wrap_comments: true + +/// Lorem ipsum dolor sit amet. +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] +pub struct BufferAttr { + /* NOTE: Blah blah blah blah blah. */ + /// Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + /// ut labore et dolore magna aliqua. Morbi quis commodo odio aenean sed adipiscing. Nunc + /// congue nisi vitae suscipit tellus mauris a. Consectetur adipiscing elit pellentesque + /// habitant morbi tristique senectus. + pub foo: u32, + + /// Elit eget gravida cum sociis natoque penatibus et magnis dis. Consequat semper viverra nam + /// libero. Accumsan in nisl nisi scelerisque eu. Pellentesque id nibh tortor id aliquet. Sed + /// velit dignissim sodales ut. Facilisis sed odio morbi quis commodo odio aenean sed. Et + /// ultrices neque ornare aenean euismod elementum. Condimentum lacinia quis vel eros donec ac + /// odio tempor. + /// + /// Lacinia at quis risus sed vulputate odio ut enim. Etiam erat velit scelerisque in dictum. + /// Nibh tellus molestie nunc non blandit massa enim nec. Nascetur ridiculus mus mauris vitae. + pub bar: u32, + + /// Mi proin sed libero enim sed faucibus turpis. Amet consectetur adipiscing elit duis + /// tristique sollicitudin nibh sit amet. Congue quisque egestas diam in arcu cursus euismod + /// quis viverra. Cum sociis natoque penatibus et magnis dis parturient montes. Enim sit amet + /// venenatis urna cursus eget nunc scelerisque viverra. Cras semper auctor neque vitae tempus + /// quam pellentesque. Tortor posuere ac ut consequat semper viverra nam libero justo. Vitae + /// auctor eu augue ut lectus arcu bibendum at. Faucibus vitae aliquet nec ullamcorper sit amet + /// risus nullam. Maecenas accumsan lacus vel facilisis volutpat. Arcu non odio euismod + /// lacinia. + /// + /// [`FooBar::beep()`]: crate::foobar::FooBar::beep + /// [`FooBar::boop()`]: crate::foobar::FooBar::boop + /// [`foobar::BazBaq::BEEP_BOOP`]: crate::foobar::BazBaq::BEEP_BOOP + pub baz: u32, + + /// Eu consequat ac felis donec et odio pellentesque diam. Ut eu sem integer vitae justo eget. + /// Consequat ac felis donec et odio pellentesque diam volutpat. + pub baq: u32, + + /// Amet consectetur adipiscing elit pellentesque habitant. Ut morbi tincidunt augue interdum + /// velit euismod in pellentesque. Imperdiet sed euismod nisi porta lorem. Nec tincidunt + /// praesent semper feugiat. Facilisis leo vel fringilla est. Egestas diam in arcu cursus + /// euismod quis viverra. Sagittis eu volutpat odio facilisis mauris sit amet. Posuere morbi + /// leo urna molestie at. + /// + /// Pretium aenean pharetra magna ac. Nisl condimentum id venenatis a condimentum vitae. Semper + /// quis lectus nulla at volutpat diam ut venenatis tellus. Egestas tellus rutrum tellus + /// pellentesque eu tincidunt tortor aliquam. + pub foobar: u32 +} From 003eaf8fe270cb8f6915ed03ad23f4af0212d607 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 7 Mar 2022 16:37:35 -0500 Subject: [PATCH 47/64] placate rustfmt in rustfmt. --- src/parse/parser.rs | 8 +------- src/visitor.rs | 4 ++-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/parse/parser.rs b/src/parse/parser.rs index ec051d9371017..268c72649a65a 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -113,13 +113,7 @@ impl<'a> Parser<'a> { let result = catch_unwind(AssertUnwindSafe(|| { let mut parser = new_parser_from_file(sess.inner(), path, Some(span)); match parser.parse_mod(&TokenKind::Eof) { - Ok((a, - i, - ast::ModSpans { - inner_span, - inject_use_span: _ - } - )) => Some((a, i, inner_span)), + Ok((a, i, spans)) => Some((a, i, spans.inner_span)), Err(mut e) => { e.emit(); if sess.can_reset_errors() { diff --git a/src/visitor.rs b/src/visitor.rs index dcf096294f14f..3ebfa551d1cbc 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -916,9 +916,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { self.push_str(&ident_str); if let ast::ModKind::Loaded(ref items, ast::Inline::Yes, ref spans) = mod_kind { - let ast::ModSpans{ + let ast::ModSpans { inner_span, - inject_use_span: _ + inject_use_span: _, } = *spans; match self.config.brace_style() { BraceStyle::AlwaysNextLine => { From 18c0369688d5854ea67dbe29cfac3cc8380cb20e Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 10 Jan 2022 20:45:27 -0500 Subject: [PATCH 48/64] Improve mod resolution error for mods with multiple candidate files Fixes 5167 When ``a.rs`` and ``a/mod.rs`` are both present we would emit an error message telling the user that the module couldn't be found. However, this behavior is misleading because we're dealing with an ambiguous module path, not a "file not found" error. Is the file ``a.rs`` or is it ``a/mod.rs``? Rustfmt can't decide, and the user needs to resolve this ambiguity themselves. Now, the error message displayed to the user is in line with what they would see if they went to compile their code with these conflicting module names. --- src/modules.rs | 38 +++++++++++++--- src/parse/session.rs | 3 ++ tests/mod-resolver/issue-5167/src/a.rs | 0 tests/mod-resolver/issue-5167/src/a/mod.rs | 0 tests/mod-resolver/issue-5167/src/lib.rs | 1 + .../bad_path_attribute/lib.rs | 3 ++ .../module-not-found/relative_module/a.rs | 2 + .../module-not-found/relative_module/lib.rs | 1 + .../module-not-found/sibling_module/lib.rs | 2 + tests/rustfmt/main.rs | 44 +++++++++++++++++++ 10 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 tests/mod-resolver/issue-5167/src/a.rs create mode 100644 tests/mod-resolver/issue-5167/src/a/mod.rs create mode 100644 tests/mod-resolver/issue-5167/src/lib.rs create mode 100644 tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs create mode 100644 tests/mod-resolver/module-not-found/relative_module/a.rs create mode 100644 tests/mod-resolver/module-not-found/relative_module/lib.rs create mode 100644 tests/mod-resolver/module-not-found/sibling_module/lib.rs diff --git a/src/modules.rs b/src/modules.rs index 70b937b02836f..49c99403974e1 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -81,6 +81,7 @@ pub struct ModuleResolutionError { pub(crate) kind: ModuleResolutionErrorKind, } +/// Defines variants similar to those of [rustc_expand::module::ModError] #[derive(Debug, Error)] pub(crate) enum ModuleResolutionErrorKind { /// Find a file that cannot be parsed. @@ -89,6 +90,12 @@ pub(crate) enum ModuleResolutionErrorKind { /// File cannot be found. #[error("{file} does not exist")] NotFound { file: PathBuf }, + /// File a.rs and a/mod.rs both exist + #[error("file for module found at both {default_path:?} and {secondary_path:?}")] + MultipleCandidates { + default_path: PathBuf, + secondary_path: PathBuf, + }, } #[derive(Clone)] @@ -444,12 +451,31 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> { } Ok(Some(SubModKind::MultiExternal(mods_outside_ast))) } - Err(_) => Err(ModuleResolutionError { - module: mod_name.to_string(), - kind: ModuleResolutionErrorKind::NotFound { - file: self.directory.path.clone(), - }, - }), + Err(e) => match e { + ModError::FileNotFound(_, default_path, _secondary_path) => { + Err(ModuleResolutionError { + module: mod_name.to_string(), + kind: ModuleResolutionErrorKind::NotFound { file: default_path }, + }) + } + ModError::MultipleCandidates(_, default_path, secondary_path) => { + Err(ModuleResolutionError { + module: mod_name.to_string(), + kind: ModuleResolutionErrorKind::MultipleCandidates { + default_path, + secondary_path, + }, + }) + } + ModError::ParserError(_) + | ModError::CircularInclusion(_) + | ModError::ModInBlock(_) => Err(ModuleResolutionError { + module: mod_name.to_string(), + kind: ModuleResolutionErrorKind::ParseError { + file: self.directory.path.clone(), + }, + }), + }, } } diff --git a/src/parse/session.rs b/src/parse/session.rs index fb9182152d1da..87ab8fbf20abd 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -163,8 +163,11 @@ impl ParseSess { |e| { // If resloving a module relative to {dir_path}/{symbol} fails because a file // could not be found, then try to resolve the module relative to {dir_path}. + // If we still can't find the module after searching for it in {dir_path}, + // surface the original error. if matches!(e, ModError::FileNotFound(..)) && relative.is_some() { rustc_expand::module::default_submod_path(&self.parse_sess, id, None, dir_path) + .map_err(|_| e) } else { Err(e) } diff --git a/tests/mod-resolver/issue-5167/src/a.rs b/tests/mod-resolver/issue-5167/src/a.rs new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/mod-resolver/issue-5167/src/a/mod.rs b/tests/mod-resolver/issue-5167/src/a/mod.rs new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/mod-resolver/issue-5167/src/lib.rs b/tests/mod-resolver/issue-5167/src/lib.rs new file mode 100644 index 0000000000000..f21af614da057 --- /dev/null +++ b/tests/mod-resolver/issue-5167/src/lib.rs @@ -0,0 +1 @@ +mod a; diff --git a/tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs b/tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs new file mode 100644 index 0000000000000..2a63c961be8fc --- /dev/null +++ b/tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs @@ -0,0 +1,3 @@ +// module resolution fails because the path does not exist. +#[path = "path/to/does_not_exist.rs"] +mod a; diff --git a/tests/mod-resolver/module-not-found/relative_module/a.rs b/tests/mod-resolver/module-not-found/relative_module/a.rs new file mode 100644 index 0000000000000..4a1eac8965ded --- /dev/null +++ b/tests/mod-resolver/module-not-found/relative_module/a.rs @@ -0,0 +1,2 @@ +// module resolution fails because `./a/b.rs` does not exist +mod b; diff --git a/tests/mod-resolver/module-not-found/relative_module/lib.rs b/tests/mod-resolver/module-not-found/relative_module/lib.rs new file mode 100644 index 0000000000000..f21af614da057 --- /dev/null +++ b/tests/mod-resolver/module-not-found/relative_module/lib.rs @@ -0,0 +1 @@ +mod a; diff --git a/tests/mod-resolver/module-not-found/sibling_module/lib.rs b/tests/mod-resolver/module-not-found/sibling_module/lib.rs new file mode 100644 index 0000000000000..d9d9e1e3c9087 --- /dev/null +++ b/tests/mod-resolver/module-not-found/sibling_module/lib.rs @@ -0,0 +1,2 @@ +// module resolution fails because `./a.rs` does not exist +mod a; diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs index 2262ae3aaacd0..450051d2fec61 100644 --- a/tests/rustfmt/main.rs +++ b/tests/rustfmt/main.rs @@ -113,3 +113,47 @@ fn rustfmt_usage_text() { let (stdout, _) = rustfmt(&args); assert!(stdout.contains("Format Rust code\n\nusage: rustfmt [options] ...")); } + +#[test] +fn mod_resolution_error_multiple_candidate_files() { + // See also https://github.com/rust-lang/rustfmt/issues/5167 + let default_path = Path::new("tests/mod-resolver/issue-5167/src/a.rs"); + let secondary_path = Path::new("tests/mod-resolver/issue-5167/src/a/mod.rs"); + let error_message = format!( + "file for module found at both {:?} and {:?}", + default_path.canonicalize().unwrap(), + secondary_path.canonicalize().unwrap(), + ); + + let args = ["tests/mod-resolver/issue-5167/src/lib.rs"]; + let (_stdout, stderr) = rustfmt(&args); + assert!(stderr.contains(&error_message)) +} + +#[test] +fn mod_resolution_error_sibling_module_not_found() { + let args = ["tests/mod-resolver/module-not-found/sibling_module/lib.rs"]; + let (_stdout, stderr) = rustfmt(&args); + // Module resolution fails because we're unable to find `a.rs` in the same directory as lib.rs + assert!(stderr.contains("a.rs does not exist")) +} + +#[test] +fn mod_resolution_error_relative_module_not_found() { + let args = ["tests/mod-resolver/module-not-found/relative_module/lib.rs"]; + let (_stdout, stderr) = rustfmt(&args); + // The file `./a.rs` and directory `./a` both exist. + // Module resolution fails becuase we're unable to find `./a/b.rs` + #[cfg(not(windows))] + assert!(stderr.contains("a/b.rs does not exist")); + #[cfg(windows)] + assert!(stderr.contains("a\\b.rs does not exist")); +} + +#[test] +fn mod_resolution_error_path_attribute_does_not_exist() { + let args = ["tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs"]; + let (_stdout, stderr) = rustfmt(&args); + // The path attribute points to a file that does not exist + assert!(stderr.contains("does_not_exist.rs does not exist")); +} From b4de150dbc65a6bb95c86517430726c55251d0b0 Mon Sep 17 00:00:00 2001 From: Tom Milligan Date: Fri, 4 Mar 2022 18:13:08 +0000 Subject: [PATCH 49/64] fix: imports_granularity module with path containing self --- src/imports.rs | 50 ++++++++++++++++------ tests/source/imports_granularity_module.rs | 1 + tests/target/imports_granularity_module.rs | 2 + 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/imports.rs b/src/imports.rs index c60bec6d4a201..0231980948686 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -190,13 +190,17 @@ pub(crate) fn merge_use_trees(use_trees: Vec, merge_by: SharedPrefix) - continue; } - for flattened in use_tree.flatten() { + for mut flattened in use_tree.flatten() { if let Some(tree) = result .iter_mut() .find(|tree| tree.share_prefix(&flattened, merge_by)) { tree.merge(&flattened, merge_by); } else { + // If this is the first tree with this prefix, handle potential trailing ::self + if merge_by == SharedPrefix::Module { + flattened = flattened.nest_trailing_self(); + } result.push(flattened); } } @@ -208,17 +212,7 @@ pub(crate) fn flatten_use_trees(use_trees: Vec) -> Vec { use_trees .into_iter() .flat_map(UseTree::flatten) - .map(|mut tree| { - // If a path ends in `::self`, rewrite it to `::{self}`. - if let Some(UseSegment::Slf(..)) = tree.path.last() { - let self_segment = tree.path.pop().unwrap(); - tree.path.push(UseSegment::List(vec![UseTree::from_path( - vec![self_segment], - DUMMY_SP, - )])); - } - tree - }) + .map(UseTree::nest_trailing_self) .collect() } @@ -635,6 +629,18 @@ impl UseTree { self.span = self.span.to(other.span); } } + + /// If this tree ends in `::self`, rewrite it to `::{self}`. + fn nest_trailing_self(mut self) -> UseTree { + if let Some(UseSegment::Slf(..)) = self.path.last() { + let self_segment = self.path.pop().unwrap(); + self.path.push(UseSegment::List(vec![UseTree::from_path( + vec![self_segment], + DUMMY_SP, + )])); + } + self + } } fn merge_rest( @@ -1311,4 +1317,24 @@ mod test { < parse_use_tree("std::cmp::{b, e, g, f}").normalize() ); } + + #[test] + fn test_use_tree_nest_trailing_self() { + assert_eq!( + parse_use_tree("a::b::self").nest_trailing_self(), + parse_use_tree("a::b::{self}") + ); + assert_eq!( + parse_use_tree("a::b::c").nest_trailing_self(), + parse_use_tree("a::b::c") + ); + assert_eq!( + parse_use_tree("a::b::{c, d}").nest_trailing_self(), + parse_use_tree("a::b::{c, d}") + ); + assert_eq!( + parse_use_tree("a::b::{self, c}").nest_trailing_self(), + parse_use_tree("a::b::{self, c}") + ); + } } diff --git a/tests/source/imports_granularity_module.rs b/tests/source/imports_granularity_module.rs index 5a4fad5872bdd..2d7bb299aaace 100644 --- a/tests/source/imports_granularity_module.rs +++ b/tests/source/imports_granularity_module.rs @@ -4,6 +4,7 @@ use a::{b::c, d::e}; use a::{f, g::{h, i}}; use a::{j::{self, k::{self, l}, m}, n::{o::p, q}}; pub use a::{r::s, t}; +use b::{c::d, self}; #[cfg(test)] use foo::{a::b, c::d}; diff --git a/tests/target/imports_granularity_module.rs b/tests/target/imports_granularity_module.rs index 9c1387c466afa..e4e1a299e5866 100644 --- a/tests/target/imports_granularity_module.rs +++ b/tests/target/imports_granularity_module.rs @@ -10,6 +10,8 @@ use a::n::o::p; use a::n::q; pub use a::r::s; pub use a::t; +use b::c::d; +use b::{self}; use foo::e; #[cfg(test)] From 9c65db61bee3f4f47a8d889ea781902283c5b5bd Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 12 Mar 2022 01:14:38 -0500 Subject: [PATCH 50/64] Correct tracking issue link for `skip_children` Update the issue link to point to issue 3389 --- Configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index 2e2b0f7cfbecf..ecec97dca522f 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2206,7 +2206,7 @@ Don't reformat out of line modules - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: [#3389](https://github.com/rust-lang/rustfmt/issues/3386)) +- **Stable**: No (tracking issue: [#3389](https://github.com/rust-lang/rustfmt/issues/3389)) ## `single_line_if_else_max_width` From 5696e3859707d2abf12465e7bfbdcf2d9f42c8a2 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 12 Mar 2022 01:16:08 -0500 Subject: [PATCH 51/64] Add test to verify tracking issue links Now, tracking issue links are checked against the reference number listed in the link text to ensure they match. --- src/test/configuration_snippet.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/test/configuration_snippet.rs b/src/test/configuration_snippet.rs index 92949ab576a6b..c8fda7c8556db 100644 --- a/src/test/configuration_snippet.rs +++ b/src/test/configuration_snippet.rs @@ -290,3 +290,33 @@ fn get_code_blocks() -> Vec { code_blocks } + +#[test] +fn check_unstable_option_tracking_issue_numbers() { + // Ensure that tracking issue links point to the correct issue number + let tracking_issue = + regex::Regex::new(r"\(tracking issue: \[#(?P\d+)\]\((?P\S+)\)\)") + .expect("failed creating configuration pattern"); + + let lines = BufReader::new( + fs::File::open(Path::new(CONFIGURATIONS_FILE_NAME)) + .unwrap_or_else(|_| panic!("couldn't read file {}", CONFIGURATIONS_FILE_NAME)), + ) + .lines() + .map(Result::unwrap) + .enumerate(); + + for (idx, line) in lines { + if let Some(capture) = tracking_issue.captures(&line) { + let number = capture.name("number").unwrap().as_str(); + let link = capture.name("link").unwrap().as_str(); + assert!( + link.ends_with(number), + "{} on line {} does not point to issue #{}", + link, + idx + 1, + number, + ); + } + } +} From 1bb85bdf6b6dc0e205009b9f531a6220fe2031f9 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 9 Mar 2022 20:53:51 -0600 Subject: [PATCH 52/64] chore: add utility function for relative span positions --- src/parse/session.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/parse/session.rs b/src/parse/session.rs index 87ab8fbf20abd..a95324bbb0e47 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -218,6 +218,15 @@ impl ParseSess { self.parse_sess.source_map().lookup_char_pos(pos).line } + // TODO(calebcartwright): Preemptive, currently unused addition + // that will be used to support formatting scenarios that take original + // positions into account + /// Determines whether two byte positions are in the same source line. + #[allow(dead_code)] + pub(crate) fn byte_pos_same_line(&self, a: BytePos, b: BytePos) -> bool { + self.line_of_byte_pos(a) == self.line_of_byte_pos(b) + } + pub(crate) fn span_to_debug_info(&self, span: Span) -> String { self.parse_sess.source_map().span_to_diagnostic_string(span) } From c0861d3a9d33d535542bbeb48d35271161c87ebf Mon Sep 17 00:00:00 2001 From: codehorseman Date: Wed, 16 Mar 2022 20:12:30 +0800 Subject: [PATCH 53/64] resolve the conflict in compiler/rustc_session/src/parse.rs Signed-off-by: codehorseman --- src/lists.rs | 2 +- src/types.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lists.rs b/src/lists.rs index 7aa0315f18c26..29d75585eb725 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -575,7 +575,7 @@ where pub(crate) fn extract_pre_comment(pre_snippet: &str) -> (Option, ListItemCommentStyle) { let trimmed_pre_snippet = pre_snippet.trim(); // Both start and end are checked to support keeping a block comment inline with - // the item, even if there are preceeding line comments, while still supporting + // the item, even if there are preceding line comments, while still supporting // a snippet that starts with a block comment but also contains one or more // trailing single line comments. // https://github.com/rust-lang/rustfmt/issues/3025 diff --git a/src/types.rs b/src/types.rs index 5de30129266a3..a49d473a13f3f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -251,7 +251,7 @@ fn rewrite_segment( match **args { ast::GenericArgs::AngleBracketed(ref data) if !data.args.is_empty() => { // HACK: squeeze out the span between the identifier and the parameters. - // The hack is requried so that we don't remove the separator inside macro calls. + // The hack is required so that we don't remove the separator inside macro calls. // This does not work in the presence of comment, hoping that people are // sane about where to put their comment. let separator_snippet = context From c1d351f6eee7f870ce5216e824709dccbdf69dff Mon Sep 17 00:00:00 2001 From: mark Date: Sat, 22 Jan 2022 18:49:12 -0600 Subject: [PATCH 54/64] rustc_error: make ErrorReported impossible to construct There are a few places were we have to construct it, though, and a few places that are more invasive to change. To do this, we create a constructor with a long obvious name. --- src/parse/macros/lazy_static.rs | 2 +- src/parse/macros/mod.rs | 2 +- src/parse/session.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/parse/macros/lazy_static.rs b/src/parse/macros/lazy_static.rs index 4c541de04be08..a8c2feec453c8 100644 --- a/src/parse/macros/lazy_static.rs +++ b/src/parse/macros/lazy_static.rs @@ -16,7 +16,7 @@ pub(crate) fn parse_lazy_static( ($method:ident $(,)* $($arg:expr),* $(,)*) => { match parser.$method($($arg,)*) { Ok(val) => { - if parser.sess.span_diagnostic.has_errors() { + if parser.sess.span_diagnostic.has_errors().is_some() { parser.sess.span_diagnostic.reset_err_count(); return None; } else { diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs index fd738908170f8..3728f3a19b44f 100644 --- a/src/parse/macros/mod.rs +++ b/src/parse/macros/mod.rs @@ -28,7 +28,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { let mut cloned_parser = (*parser).clone(); match $parser(&mut cloned_parser) { Ok(x) => { - if parser.sess.span_diagnostic.has_errors() { + if parser.sess.span_diagnostic.has_errors().is_some() { parser.sess.span_diagnostic.reset_err_count(); } else { // Parsing succeeded. diff --git a/src/parse/session.rs b/src/parse/session.rs index 40a6d708d8ccc..a34ceed3fc91f 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -235,7 +235,7 @@ impl ParseSess { } pub(super) fn has_errors(&self) -> bool { - self.parse_sess.span_diagnostic.has_errors() + self.parse_sess.span_diagnostic.has_errors().is_some() } pub(super) fn reset_errors(&self) { From 432b8dea64b84cec3efc3205bdc9e6687d59812d Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 16 Mar 2022 22:26:15 -0500 Subject: [PATCH 55/64] chore: bump toolchain --- Cargo.lock | 4 ++-- rust-toolchain | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2ef83ddd1ae6c..b932e15ef7461 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -275,9 +275,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.9.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" dependencies = [ "either", ] diff --git a/rust-toolchain b/rust-toolchain index d8bf02aec85e2..0d407f11994bb 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-01-23" +channel = "nightly-2022-03-17" components = ["rustc-dev"] From 4f89c51a5b3f27f38a7701d5243a80d7c1c44dff Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 20 Mar 2022 18:26:09 +0100 Subject: [PATCH 56/64] Take &mut Diagnostic in emit_diagnostic. Taking a Diagnostic by move would break the usual pattern `diag.label(..).emit()`. --- src/parse/session.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/parse/session.rs b/src/parse/session.rs index a34ceed3fc91f..412f4434b9ead 100644 --- a/src/parse/session.rs +++ b/src/parse/session.rs @@ -225,8 +225,10 @@ impl ParseSess { // Methods that should be restricted within the parse module. impl ParseSess { pub(super) fn emit_diagnostics(&self, diagnostics: Vec) { - for diagnostic in diagnostics { - self.parse_sess.span_diagnostic.emit_diagnostic(&diagnostic); + for mut diagnostic in diagnostics { + self.parse_sess + .span_diagnostic + .emit_diagnostic(&mut diagnostic); } } From e41329ce87409df929ecfa191297f944472cc999 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 20 Mar 2022 13:21:44 -0400 Subject: [PATCH 57/64] Search for struct body span after any generic arguments Fixes 5273 Previously, rustfmt searched for the start of a struct body after the opening `{`. In most cases this works just fine, but const values can also be defined between `{ }`, which lead to issues when rewriting the struct body. Now, rustfmt will search for the `{` after the generic argument list to guarantee that the `{` it finds is the start of the struct body. --- src/items.rs | 8 +++++++- tests/target/issue_5273.rs | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/target/issue_5273.rs diff --git a/src/items.rs b/src/items.rs index 9b35d28f11950..92f423bbb6275 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1273,7 +1273,13 @@ pub(crate) fn format_struct_struct( result.push_str(&header_str); let header_hi = struct_parts.ident.span.hi(); - let body_lo = context.snippet_provider.span_after(span, "{"); + let body_lo = if let Some(generics) = struct_parts.generics { + // Adjust the span to start at the end of the generic arguments before searching for the '{' + let span = span.with_lo(generics.span.hi()); + context.snippet_provider.span_after(span, "{") + } else { + context.snippet_provider.span_after(span, "{") + }; let generics_str = match struct_parts.generics { Some(g) => format_generics( diff --git a/tests/target/issue_5273.rs b/tests/target/issue_5273.rs new file mode 100644 index 0000000000000..3bb9048a5fd3d --- /dev/null +++ b/tests/target/issue_5273.rs @@ -0,0 +1,3 @@ +struct Example { + // +} From 0dba01aee15a45b8417c06df69e38af6956e03a8 Mon Sep 17 00:00:00 2001 From: 123vivekr <123vivekr@gmail.com> Date: Sun, 13 Feb 2022 17:54:09 +0530 Subject: [PATCH 58/64] Add `short_item_threshold` config option Allow custom short item threshold values via config --- Configurations.md | 34 +++++++++++++++++++ src/config/mod.rs | 3 ++ src/overflow.rs | 13 ++++--- .../short_array_element_width_threshold/10.rs | 11 ++++++ .../short_array_element_width_threshold/20.rs | 11 ++++++ .../greater_than_max_width.rs | 12 +++++++ .../short_array_element_width_threshold/10.rs | 11 ++++++ .../short_array_element_width_threshold/20.rs | 8 +++++ .../greater_than_max_width.rs | 12 +++++++ 9 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 tests/source/configs/short_array_element_width_threshold/10.rs create mode 100644 tests/source/configs/short_array_element_width_threshold/20.rs create mode 100644 tests/source/configs/short_array_element_width_threshold/greater_than_max_width.rs create mode 100644 tests/target/configs/short_array_element_width_threshold/10.rs create mode 100644 tests/target/configs/short_array_element_width_threshold/20.rs create mode 100644 tests/target/configs/short_array_element_width_threshold/greater_than_max_width.rs diff --git a/Configurations.md b/Configurations.md index ecec97dca522f..a47439b9ba96c 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2200,6 +2200,40 @@ specific version of rustfmt is used in your CI, use this option. - **Possible values**: any published version (e.g. `"0.3.8"`) - **Stable**: No (tracking issue: [#3386](https://github.com/rust-lang/rustfmt/issues/3386)) +## `short_array_element_width_threshold` + +The width threshold for an array element to be considered "short". + +The layout of an array is dependent on the length of each of its elements. +If the length of every element in an array is below this threshold (all elements are "short") then the array can be formatted in the mixed/compressed style, but if any one element has a length that exceeds this threshold then the array elements will have to be formatted vertically. + +- **Default value**: `10` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +#### `10` (default): +```rust +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} +``` +#### `20`: +```rust +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, 0xaaaaaaaaaaaaaaaa, 0xbbbbbbbbbbbbbbbb, 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} +``` +See also [`max_width`](#max_width). + ## `skip_children` Don't reformat out of line modules diff --git a/src/config/mod.rs b/src/config/mod.rs index 5041e1e36dd6d..18e1854612bf7 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -106,6 +106,8 @@ create_config! { // Misc. remove_nested_parens: bool, true, true, "Remove nested parens"; combine_control_expr: bool, true, false, "Combine control expressions with function calls"; + short_array_element_width_threshold: usize, 10, true, + "Width threshold for an array element to be considered short"; overflow_delimited_expr: bool, false, false, "Allow trailing bracket/brace delimited expressions to overflow"; struct_field_align_threshold: usize, 0, false, @@ -591,6 +593,7 @@ spaces_around_ranges = false binop_separator = "Front" remove_nested_parens = true combine_control_expr = true +short_array_element_width_threshold = 10 overflow_delimited_expr = false struct_field_align_threshold = 0 enum_discrim_align_threshold = 0 diff --git a/src/overflow.rs b/src/overflow.rs index 3475f5c378cd2..80aed998d7377 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -26,8 +26,6 @@ use crate::spanned::Spanned; use crate::types::{can_be_overflowed_type, SegmentParam}; use crate::utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp}; -const SHORT_ITEM_THRESHOLD: usize = 10; - /// A list of `format!`-like macros, that take a long format string and a list of arguments to /// format. /// @@ -572,7 +570,12 @@ impl<'a> Context<'a> { if one_line { tactic = DefinitiveListTactic::SpecialMacro(num_args_before); }; - } else if is_every_expr_simple(&self.items) && no_long_items(list_items) { + } else if is_every_expr_simple(&self.items) + && no_long_items( + list_items, + self.context.config.short_array_element_width_threshold(), + ) + { tactic = DefinitiveListTactic::Mixed; } } @@ -755,9 +758,9 @@ fn shape_from_indent_style( } } -fn no_long_items(list: &[ListItem]) -> bool { +fn no_long_items(list: &[ListItem], short_array_element_width_threshold: usize) -> bool { list.iter() - .all(|item| item.inner_as_ref().len() <= SHORT_ITEM_THRESHOLD) + .all(|item| item.inner_as_ref().len() <= short_array_element_width_threshold) } /// In case special-case style is required, returns an offset from which we start horizontal layout. diff --git a/tests/source/configs/short_array_element_width_threshold/10.rs b/tests/source/configs/short_array_element_width_threshold/10.rs new file mode 100644 index 0000000000000..7d0d70919a607 --- /dev/null +++ b/tests/source/configs/short_array_element_width_threshold/10.rs @@ -0,0 +1,11 @@ +// rustfmt-short_array_element_width_threshold: 10 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} \ No newline at end of file diff --git a/tests/source/configs/short_array_element_width_threshold/20.rs b/tests/source/configs/short_array_element_width_threshold/20.rs new file mode 100644 index 0000000000000..8a93a51d6a281 --- /dev/null +++ b/tests/source/configs/short_array_element_width_threshold/20.rs @@ -0,0 +1,11 @@ +// rustfmt-short_array_element_width_threshold: 20 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} \ No newline at end of file diff --git a/tests/source/configs/short_array_element_width_threshold/greater_than_max_width.rs b/tests/source/configs/short_array_element_width_threshold/greater_than_max_width.rs new file mode 100644 index 0000000000000..710b6fe7c4ba0 --- /dev/null +++ b/tests/source/configs/short_array_element_width_threshold/greater_than_max_width.rs @@ -0,0 +1,12 @@ +// rustfmt-max_width: 20 +// rustfmt-short_array_element_width_threshold: 30 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} diff --git a/tests/target/configs/short_array_element_width_threshold/10.rs b/tests/target/configs/short_array_element_width_threshold/10.rs new file mode 100644 index 0000000000000..78c4adba1c1f1 --- /dev/null +++ b/tests/target/configs/short_array_element_width_threshold/10.rs @@ -0,0 +1,11 @@ +// rustfmt-short_array_element_width_threshold: 10 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} diff --git a/tests/target/configs/short_array_element_width_threshold/20.rs b/tests/target/configs/short_array_element_width_threshold/20.rs new file mode 100644 index 0000000000000..6084690652f06 --- /dev/null +++ b/tests/target/configs/short_array_element_width_threshold/20.rs @@ -0,0 +1,8 @@ +// rustfmt-short_array_element_width_threshold: 20 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, 0xaaaaaaaaaaaaaaaa, 0xbbbbbbbbbbbbbbbb, 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} diff --git a/tests/target/configs/short_array_element_width_threshold/greater_than_max_width.rs b/tests/target/configs/short_array_element_width_threshold/greater_than_max_width.rs new file mode 100644 index 0000000000000..710b6fe7c4ba0 --- /dev/null +++ b/tests/target/configs/short_array_element_width_threshold/greater_than_max_width.rs @@ -0,0 +1,12 @@ +// rustfmt-max_width: 20 +// rustfmt-short_array_element_width_threshold: 30 + +fn main() { + pub const FORMAT_TEST: [u64; 5] = [ + 0x0000000000000000, + 0xaaaaaaaaaaaaaaaa, + 0xbbbbbbbbbbbbbbbb, + 0xcccccccccccccccc, + 0xdddddddddddddddd, + ]; +} From 8984438a6faf11e0cb8e876e80f177a42a43904d Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Fri, 18 Mar 2022 09:48:45 -0400 Subject: [PATCH 59/64] Honor `#[rustfmt::skip::attributes(derive)]` attribute Fixes 5270 Previously, rustfmt only checked the `merge_derives` configuration value to determine if it should merge_derives. This lead to derives being merged even when annotated with the `rustfmt::skip` attribute. Now, rustfmt also checks if derives are explicitly being skipped in the current context via the `rustfmt::skip` attribute. --- src/attr.rs | 6 +- tests/source/issue-5270/merge_derives_true.rs | 62 +++++++++++++++++++ .../target/issue-5270/merge_derives_false.rs | 62 +++++++++++++++++++ tests/target/issue-5270/merge_derives_true.rs | 60 ++++++++++++++++++ 4 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 tests/source/issue-5270/merge_derives_true.rs create mode 100644 tests/target/issue-5270/merge_derives_false.rs create mode 100644 tests/target/issue-5270/merge_derives_true.rs diff --git a/src/attr.rs b/src/attr.rs index 3887a8051f209..befe12ae2c4cd 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -389,6 +389,10 @@ impl Rewrite for [ast::Attribute] { let mut attrs = self; let mut result = String::new(); + // Determine if the source text is annotated with `#[rustfmt::skip::attributes(derive)]` + // or `#![rustfmt::skip::attributes(derive)]` + let skip_derives = context.skip_context.skip_attribute("derive"); + // This is not just a simple map because we need to handle doc comments // (where we take as many doc comment attributes as possible) and possibly // merging derives into a single attribute. @@ -431,7 +435,7 @@ impl Rewrite for [ast::Attribute] { } // Handle derives if we will merge them. - if context.config.merge_derives() && is_derive(&attrs[0]) { + if !skip_derives && context.config.merge_derives() && is_derive(&attrs[0]) { let derives = take_while_with_pred(context, attrs, is_derive); let derive_str = format_derive(derives, shape, context)?; result.push_str(&derive_str); diff --git a/tests/source/issue-5270/merge_derives_true.rs b/tests/source/issue-5270/merge_derives_true.rs new file mode 100644 index 0000000000000..b31bbf095e730 --- /dev/null +++ b/tests/source/issue-5270/merge_derives_true.rs @@ -0,0 +1,62 @@ +// rustfmt-merge_derives:true + +#[rustfmt::skip::attributes(derive)] +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +struct DoNotMergeDerives { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[rustfmt::skip::attributes(derive)] +#[derive(Clone)] +struct DoNotMergeDerivesSkipInMiddle { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +#[rustfmt::skip::attributes(derive)] +struct DoNotMergeDerivesSkipAtEnd { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +struct MergeDerives { + field: String, +} + +mod inner_attribute_derive_skip { + #![rustfmt::skip::attributes(derive)] + + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +#[rustfmt::skip::attributes(derive)] +mod outer_attribute_derive_skip { + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +mod no_derive_skip { + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct MergeDerives { + field: String, + } +} diff --git a/tests/target/issue-5270/merge_derives_false.rs b/tests/target/issue-5270/merge_derives_false.rs new file mode 100644 index 0000000000000..3b6f7e66993c0 --- /dev/null +++ b/tests/target/issue-5270/merge_derives_false.rs @@ -0,0 +1,62 @@ +// rustfmt-merge_derives:false + +#[rustfmt::skip::attributes(derive)] +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +struct DoNotMergeDerives { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[rustfmt::skip::attributes(derive)] +#[derive(Clone)] +struct DoNotMergeDerivesSkipInMiddle { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +#[rustfmt::skip::attributes(derive)] +struct DoNotMergeDerivesSkipAtEnd { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +struct MergeDerives { + field: String, +} + +mod inner_attribute_derive_skip { + #![rustfmt::skip::attributes(derive)] + + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +#[rustfmt::skip::attributes(derive)] +mod outer_attribute_derive_skip { + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +mod no_derive_skip { + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct MergeDerives { + field: String, + } +} diff --git a/tests/target/issue-5270/merge_derives_true.rs b/tests/target/issue-5270/merge_derives_true.rs new file mode 100644 index 0000000000000..5f488b4542d0b --- /dev/null +++ b/tests/target/issue-5270/merge_derives_true.rs @@ -0,0 +1,60 @@ +// rustfmt-merge_derives:true + +#[rustfmt::skip::attributes(derive)] +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +struct DoNotMergeDerives { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[rustfmt::skip::attributes(derive)] +#[derive(Clone)] +struct DoNotMergeDerivesSkipInMiddle { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField)] +#[derive(Clone)] +#[rustfmt::skip::attributes(derive)] +struct DoNotMergeDerivesSkipAtEnd { + field: String, +} + +#[allow(dead_code)] +#[derive(StructField, Clone)] +struct MergeDerives { + field: String, +} + +mod inner_attribute_derive_skip { + #![rustfmt::skip::attributes(derive)] + + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +#[rustfmt::skip::attributes(derive)] +mod outer_attribute_derive_skip { + #[allow(dead_code)] + #[derive(StructField)] + #[derive(Clone)] + struct DoNotMergeDerives { + field: String, + } +} + +mod no_derive_skip { + #[allow(dead_code)] + #[derive(StructField, Clone)] + struct MergeDerives { + field: String, + } +} From c2039d95c6fd5c27a90e768e79c630af374fcaf0 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 27 Mar 2022 20:46:25 -0500 Subject: [PATCH 60/64] chore: bump toolchain --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 0d407f11994bb..94b57d506c20b 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-03-17" +channel = "nightly-2022-03-27" components = ["rustc-dev"] From 4fecede7fdaeeb859b066394bf27401fd13743a7 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 28 Mar 2022 20:27:42 -0500 Subject: [PATCH 61/64] Revert "Use cargo-fmt in self_tests" This reverts commit c63d42e80473a0c18714b55058f27506fd24955c. --- src/test/mod.rs | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/src/test/mod.rs b/src/test/mod.rs index 4191e3e96b0a8..ab966d4a36075 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -375,21 +375,43 @@ fn idempotence_tests() { }); } +// Run rustfmt on itself. This operation must be idempotent. We also check that +// no warnings are emitted. +// Issue-3443: these tests require nightly #[nightly_only_test] #[test] fn self_tests() { - let get_exe_path = |name| { - let mut path = env::current_exe().unwrap(); - path.pop(); - path.set_file_name(format!("{name}{}", env::consts::EXE_SUFFIX)); - path - }; - let status = Command::new(get_exe_path("cargo-fmt")) - .args(["--check", "--all"]) - .env("RUSTFMT", get_exe_path("rustfmt")) - .status() - .unwrap(); - assert!(status.success()); + init_log(); + let mut files = get_test_files(Path::new("tests"), false); + let bin_directories = vec!["cargo-fmt", "git-rustfmt", "bin", "format-diff"]; + for dir in bin_directories { + let mut path = PathBuf::from("src"); + path.push(dir); + path.push("main.rs"); + files.push(path); + } + files.push(PathBuf::from("src/lib.rs")); + + let (reports, count, fails) = check_files(files, &Some(PathBuf::from("rustfmt.toml"))); + let mut warnings = 0; + + // Display results. + println!("Ran {} self tests.", count); + assert_eq!(fails, 0, "{} self tests failed", fails); + + for format_report in reports { + println!( + "{}", + FormatReportFormatterBuilder::new(&format_report).build() + ); + warnings += format_report.warning_count(); + } + + assert_eq!( + warnings, 0, + "Rustfmt's code generated {} warnings", + warnings + ); } #[test] From e0c7b7d5d30d5b47f9d6a9fd3c449fc610a330b9 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 28 Mar 2022 20:29:05 -0500 Subject: [PATCH 62/64] tests: ignore cargo fmt test for rust-lang/rust runs --- tests/cargo-fmt/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/cargo-fmt/main.rs b/tests/cargo-fmt/main.rs index 3713552c66af9..348876cd264fa 100644 --- a/tests/cargo-fmt/main.rs +++ b/tests/cargo-fmt/main.rs @@ -73,6 +73,7 @@ fn rustfmt_help() { assert_that!(&["--", "--help=config"], contains("Configuration Options:")); } +#[ignore] #[test] fn cargo_fmt_out_of_line_test_modules() { // See also https://github.com/rust-lang/rustfmt/issues/5119 From 8e94761a94162f43e6c3e9bfbbc5005da3dbeea5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 29 Mar 2022 13:09:28 -0700 Subject: [PATCH 63/64] Add test of macro calls inside extern block --- tests/source/extern.rs | 13 +++++++++++++ tests/target/extern.rs | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/tests/source/extern.rs b/tests/source/extern.rs index d0a033b12432a..5b981385d2b1b 100644 --- a/tests/source/extern.rs +++ b/tests/source/extern.rs @@ -77,3 +77,16 @@ libc::c_long; extern { } + +macro_rules! x { + ($tt:tt) => {}; +} + +extern "macros" { + x!(ident); + // x!(#); FIXME + x![ident]; + // x![#]; FIXME + x! {ident} + x! {#} +} diff --git a/tests/target/extern.rs b/tests/target/extern.rs index 44ed6d4b4756d..570d21c17dfce 100644 --- a/tests/target/extern.rs +++ b/tests/target/extern.rs @@ -82,3 +82,16 @@ extern "C" { } extern "C" {} + +macro_rules! x { + ($tt:tt) => {}; +} + +extern "macros" { + x!(ident); + // x!(#); FIXME + x![ident]; + // x![#]; FIXME + x! {ident} + x! {#} +} From 5ff7b632a95bac6955611d85040859128902c580 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 29 Mar 2022 12:59:07 -0700 Subject: [PATCH 64/64] Preserve semicolon after macro call inside foreign mod --- src/macros.rs | 14 ++++++++++++-- tests/source/extern.rs | 4 ++-- tests/target/extern.rs | 4 ++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index fdbe3374615cd..664f152e8be1d 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -112,6 +112,7 @@ fn rewrite_macro_name( fn return_macro_parse_failure_fallback( context: &RewriteContext<'_>, indent: Indent, + position: MacroPosition, span: Span, ) -> Option { // Mark this as a failure however we format it @@ -140,7 +141,11 @@ fn return_macro_parse_failure_fallback( )); // Return the snippet unmodified if the macro is not block-like - Some(context.snippet(span).to_owned()) + let mut snippet = context.snippet(span).to_owned(); + if position == MacroPosition::Item { + snippet.push(';'); + } + Some(snippet) } pub(crate) fn rewrite_macro( @@ -233,7 +238,12 @@ fn rewrite_macro_inner( } = match parse_macro_args(context, ts, style, is_forced_bracket) { Some(args) => args, None => { - return return_macro_parse_failure_fallback(context, shape.indent, mac.span()); + return return_macro_parse_failure_fallback( + context, + shape.indent, + position, + mac.span(), + ); } }; diff --git a/tests/source/extern.rs b/tests/source/extern.rs index 5b981385d2b1b..f51ba6e98c9f5 100644 --- a/tests/source/extern.rs +++ b/tests/source/extern.rs @@ -84,9 +84,9 @@ macro_rules! x { extern "macros" { x!(ident); - // x!(#); FIXME + x!(#); x![ident]; - // x![#]; FIXME + x![#]; x! {ident} x! {#} } diff --git a/tests/target/extern.rs b/tests/target/extern.rs index 570d21c17dfce..d1741360cfd64 100644 --- a/tests/target/extern.rs +++ b/tests/target/extern.rs @@ -89,9 +89,9 @@ macro_rules! x { extern "macros" { x!(ident); - // x!(#); FIXME + x!(#); x![ident]; - // x![#]; FIXME + x![#]; x! {ident} x! {#} }