From 6db6bafc61b6f4f782c5f10e8dbe3f006c41fc5d Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 29 Dec 2021 20:49:39 -0600 Subject: Merge commit '4a053f206fd6799a25823c307f7d7f9d897be118' into sync-rustfmt-subtree --- 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/parse/parser.rs | 175 ++++++++++++++ src/parse/session.rs | 465 ++++++++++++++++++++++++++++++++++++++ src/patterns.rs | 7 +- src/rewrite.rs | 2 +- src/rustfmt_diff.rs | 10 +- src/source_file.rs | 2 +- src/syntux/parser.rs | 258 --------------------- src/syntux/session.rs | 465 -------------------------------------- src/test/configuration_snippet.rs | 21 +- src/test/mod_resolver.rs | 9 + src/visitor.rs | 27 +-- 29 files changed, 1191 insertions(+), 1044 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 create mode 100644 src/parse/parser.rs create mode 100644 src/parse/session.rs delete mode 100644 src/syntux/parser.rs delete mode 100644 src/syntux/session.rs (limited to 'src') diff --git a/src/bin/main.rs b/src/bin/main.rs index 9d2e97c9479..4d845547cdf 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 34d73a77fd3..e688db1c39d 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 4b799780d85..7b498dc46b3 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 bce9e5d07f2..d857c29be29 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 5fd86c1a4ea..c9c8852cd3b 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 7d0facb8f12..67cf1232f66 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 b7dd6b06ff8..babc56f86ed 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 792a1080f0e..ad23b16e02e 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 3515dd17251..7aa0315f18c 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 a52568be9ea..f29552caf8d 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 22d23fc1cdb..85d9c5d2b9b 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 9d438a80d94..9c964b274e0 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 d5acf3f1cbc..ea67977c17a 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 00000000000..cc9fb5072ce --- /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 00000000000..e10fbe64bcd --- /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 00000000000..9c8651aa3fa --- /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 00000000000..2e9ce1d35f4 --- /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 00000000000..5e88826ea8c --- /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/parse/parser.rs b/src/parse/parser.rs new file mode 100644 index 00000000000..657217633f4 --- /dev/null +++ b/src/parse/parser.rs @@ -0,0 +1,175 @@ +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::path::{Path, PathBuf}; + +use rustc_ast::token::TokenKind; +use rustc_ast::{ast, ptr}; +use rustc_errors::Diagnostic; +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::parse::session::ParseSess; +use crate::Input; + +pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership; +pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess; +pub(crate) type ModError<'a> = rustc_expand::module::ModError<'a>; + +#[derive(Clone)] +pub(crate) struct Directory { + pub(crate) path: PathBuf, + pub(crate) ownership: DirectoryOwnership, +} + +/// A parser for Rust source code. +pub(crate) struct Parser<'a> { + parser: RawParser<'a>, +} + +/// A builder for the `Parser`. +#[derive(Default)] +pub(crate) struct ParserBuilder<'a> { + sess: Option<&'a ParseSess>, + input: Option, +} + +impl<'a> ParserBuilder<'a> { + pub(crate) fn input(mut self, input: Input) -> ParserBuilder<'a> { + self.input = Some(input); + self + } + + pub(crate) fn sess(mut self, sess: &'a ParseSess) -> ParserBuilder<'a> { + self.sess = Some(sess); + self + } + + pub(crate) fn build(self) -> Result, ParserError> { + let sess = self.sess.ok_or(ParserError::NoParseSess)?; + let input = self.input.ok_or(ParserError::NoInput)?; + + let parser = match Self::parser(sess.inner(), input) { + Ok(p) => p, + Err(db) => { + if let Some(diagnostics) = db { + sess.emit_diagnostics(diagnostics); + return Err(ParserError::ParserCreationError); + } + return Err(ParserError::ParsePanicError); + } + }; + + Ok(Parser { parser }) + } + + fn parser( + sess: &'a rustc_session::parse::ParseSess, + input: Input, + ) -> Result, Option>> { + match input { + Input::File(ref file) => catch_unwind(AssertUnwindSafe(move || { + new_parser_from_file(sess, file, None) + })) + .map_err(|_| None), + Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str( + sess, + rustc_span::FileName::Custom("stdin".to_owned()), + text, + ) + .map_err(Some), + } + } +} + +#[derive(Debug, PartialEq)] +pub(crate) enum ParserError { + NoParseSess, + NoInput, + ParserCreationError, + ParseError, + ParsePanicError, +} + +impl<'a> Parser<'a> { + pub(crate) fn submod_path_from_attr(attrs: &[ast::Attribute], path: &Path) -> Option { + let path_sym = first_attr_value_str_by_name(attrs, sym::path)?; + let path_str = path_sym.as_str(); + + // On windows, the base path might have the form + // `\\?\foo\bar` in which case it does not tolerate + // mixed `/` and `\` separators, so canonicalize + // `/` to `\`. + #[cfg(windows)] + let path_str = path_str.replace("/", "\\"); + + Some(path.join(path_str)) + } + + pub(crate) fn parse_file_as_module( + sess: &'a ParseSess, + path: &Path, + span: Span, + ) -> Result<(Vec, Vec>, Span), ParserError> { + 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), + Err(mut e) => { + sess.emit_or_cancel_diagnostic(&mut e); + if sess.can_reset_errors() { + sess.reset_errors(); + } + None + } + } + })); + match result { + Ok(Some(m)) if !sess.has_errors() => Ok(m), + Ok(Some(m)) if sess.can_reset_errors() => { + sess.reset_errors(); + Ok(m) + } + Ok(_) => Err(ParserError::ParseError), + Err(..) if path.exists() => Err(ParserError::ParseError), + Err(_) => Err(ParserError::ParsePanicError), + } + } + + pub(crate) fn parse_crate( + input: Input, + sess: &'a ParseSess, + ) -> Result { + let krate = Parser::parse_crate_inner(input, sess)?; + if !sess.has_errors() { + return Ok(krate); + } + + if sess.can_reset_errors() { + sess.reset_errors(); + return Ok(krate); + } + + Err(ParserError::ParseError) + } + + fn parse_crate_inner(input: Input, sess: &'a ParseSess) -> Result { + ParserBuilder::default() + .input(input) + .sess(sess) + .build()? + .parse_crate_mod() + } + + fn parse_crate_mod(&mut self) -> Result { + let mut parser = AssertUnwindSafe(&mut self.parser); + + match catch_unwind(move || parser.parse_crate_mod()) { + Ok(Ok(k)) => Ok(k), + Ok(Err(mut db)) => { + db.emit(); + Err(ParserError::ParseError) + } + Err(_) => Err(ParserError::ParsePanicError), + } + } +} diff --git a/src/parse/session.rs b/src/parse/session.rs new file mode 100644 index 00000000000..624fed0d2de --- /dev/null +++ b/src/parse/session.rs @@ -0,0 +1,465 @@ +use std::path::Path; +use std::sync::atomic::{AtomicBool, Ordering}; + +use rustc_data_structures::sync::{Lrc, Send}; +use rustc_errors::emitter::{Emitter, EmitterWriter}; +use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel}; +use rustc_session::parse::ParseSess as RawParseSess; +use rustc_span::{ + source_map::{FilePathMapping, SourceMap}, + symbol, BytePos, Span, +}; + +use crate::config::file_lines::LineRange; +use crate::ignore_path::IgnorePathSet; +use crate::source_map::LineRangeUtils; +use crate::utils::starts_with_newline; +use crate::visitor::SnippetProvider; +use crate::{Config, ErrorKind, FileName}; + +/// ParseSess holds structs necessary for constructing a parser. +pub(crate) struct ParseSess { + parse_sess: RawParseSess, + ignore_path_set: Lrc, + can_reset_errors: Lrc, +} + +/// Emitter which discards every error. +struct SilentEmitter; + +impl Emitter for SilentEmitter { + fn source_map(&self) -> Option<&Lrc> { + None + } + fn emit_diagnostic(&mut self, _db: &Diagnostic) {} +} + +fn silent_emitter() -> Box { + Box::new(SilentEmitter {}) +} + +/// Emit errors against every files expect ones specified in the `ignore_path_set`. +struct SilentOnIgnoredFilesEmitter { + ignore_path_set: Lrc, + source_map: Lrc, + emitter: Box, + has_non_ignorable_parser_errors: bool, + can_reset: Lrc, +} + +impl SilentOnIgnoredFilesEmitter { + fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) { + self.has_non_ignorable_parser_errors = true; + self.can_reset.store(false, Ordering::Release); + self.emitter.emit_diagnostic(db); + } +} + +impl Emitter for SilentOnIgnoredFilesEmitter { + fn source_map(&self) -> Option<&Lrc> { + None + } + fn emit_diagnostic(&mut self, db: &Diagnostic) { + if db.level == DiagnosticLevel::Fatal { + return self.handle_non_ignoreable_error(db); + } + if let Some(primary_span) = &db.span.primary_span() { + let file_name = self.source_map.span_to_filename(*primary_span); + if let rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(ref path)) = + file_name + { + if self + .ignore_path_set + .is_match(&FileName::Real(path.to_path_buf())) + { + if !self.has_non_ignorable_parser_errors { + self.can_reset.store(true, Ordering::Release); + } + return; + } + }; + } + self.handle_non_ignoreable_error(db); + } +} + +fn default_handler( + source_map: Lrc, + ignore_path_set: Lrc, + can_reset: Lrc, + hide_parse_errors: bool, +) -> Handler { + let supports_color = term::stderr().map_or(false, |term| term.supports_color()); + let color_cfg = if supports_color { + ColorConfig::Auto + } else { + ColorConfig::Never + }; + + let emitter = if hide_parse_errors { + silent_emitter() + } else { + Box::new(EmitterWriter::stderr( + color_cfg, + Some(source_map.clone()), + false, + false, + None, + false, + )) + }; + Handler::with_emitter( + true, + None, + Box::new(SilentOnIgnoredFilesEmitter { + has_non_ignorable_parser_errors: false, + source_map, + emitter, + ignore_path_set, + can_reset, + }), + ) +} + +impl ParseSess { + pub(crate) fn new(config: &Config) -> Result { + let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) { + Ok(ignore_path_set) => Lrc::new(ignore_path_set), + Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)), + }; + let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let can_reset_errors = Lrc::new(AtomicBool::new(false)); + + let handler = default_handler( + Lrc::clone(&source_map), + Lrc::clone(&ignore_path_set), + Lrc::clone(&can_reset_errors), + config.hide_parse_errors(), + ); + let parse_sess = RawParseSess::with_span_handler(handler, source_map); + + Ok(ParseSess { + parse_sess, + ignore_path_set, + can_reset_errors, + }) + } + + 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) + } + + pub(crate) fn is_file_parsed(&self, path: &Path) -> bool { + self.parse_sess + .source_map() + .get_source_file(&rustc_span::FileName::Real( + rustc_span::RealFileName::LocalPath(path.to_path_buf()), + )) + .is_some() + } + + pub(crate) fn ignore_file(&self, path: &FileName) -> bool { + self.ignore_path_set.as_ref().is_match(path) + } + + pub(crate) fn set_silent_emitter(&mut self) { + self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter()); + } + + pub(crate) fn span_to_filename(&self, span: Span) -> FileName { + self.parse_sess.source_map().span_to_filename(span).into() + } + + pub(crate) fn span_to_file_contents(&self, span: Span) -> Lrc { + self.parse_sess + .source_map() + .lookup_source_file(span.data().lo) + } + + pub(crate) fn span_to_first_line_string(&self, span: Span) -> String { + let file_lines = self.parse_sess.source_map().span_to_lines(span).ok(); + + match file_lines { + Some(fl) => fl + .file + .get_line(fl.lines[0].line_index) + .map_or_else(String::new, |s| s.to_string()), + None => String::new(), + } + } + + pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize { + self.parse_sess.source_map().lookup_char_pos(pos).line + } + + pub(crate) fn span_to_debug_info(&self, span: Span) -> String { + self.parse_sess.source_map().span_to_diagnostic_string(span) + } + + pub(crate) fn inner(&self) -> &RawParseSess { + &self.parse_sess + } + + pub(crate) fn snippet_provider(&self, span: Span) -> SnippetProvider { + let source_file = self.parse_sess.source_map().lookup_char_pos(span.lo()).file; + SnippetProvider::new( + source_file.start_pos, + source_file.end_pos, + Lrc::clone(source_file.src.as_ref().unwrap()), + ) + } + + pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option> { + self.parse_sess + .source_map() + .get_source_file(&file_name.into()) + .and_then(|source_file| source_file.src.clone()) + } +} + +// 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); + } + } + + 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) + } + + pub(super) fn has_errors(&self) -> bool { + self.parse_sess.span_diagnostic.has_errors() + } + + pub(super) fn reset_errors(&self) { + self.parse_sess.span_diagnostic.reset_err_count(); + } +} + +impl LineRangeUtils for ParseSess { + fn lookup_line_range(&self, span: Span) -> LineRange { + let snippet = self + .parse_sess + .source_map() + .span_to_snippet(span) + .unwrap_or_default(); + let lo = self.parse_sess.source_map().lookup_line(span.lo()).unwrap(); + let hi = self.parse_sess.source_map().lookup_line(span.hi()).unwrap(); + + debug_assert_eq!( + lo.sf.name, hi.sf.name, + "span crossed file boundary: lo: {:?}, hi: {:?}", + lo, hi + ); + + // in case the span starts with a newline, the line range is off by 1 without the + // adjustment below + let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 }; + // Line numbers start at 1 + LineRange { + file: lo.sf.clone(), + lo: lo.line + offset, + hi: hi.line + offset, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use rustfmt_config_proc_macro::nightly_only_test; + + mod emitter { + use super::*; + use crate::config::IgnoreList; + use crate::utils::mk_sp; + use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName, DUMMY_SP}; + use std::path::PathBuf; + use std::sync::atomic::AtomicU32; + + struct TestEmitter { + num_emitted_errors: Lrc, + } + + impl Emitter for TestEmitter { + fn source_map(&self) -> Option<&Lrc> { + None + } + fn emit_diagnostic(&mut self, _db: &Diagnostic) { + self.num_emitted_errors.fetch_add(1, Ordering::Release); + } + } + + fn build_diagnostic(level: DiagnosticLevel, span: Option) -> Diagnostic { + Diagnostic { + level, + code: None, + message: vec![], + children: vec![], + suggestions: vec![], + span: span.unwrap_or_else(MultiSpan::new), + sort_span: DUMMY_SP, + is_lint: false, + } + } + + fn build_emitter( + num_emitted_errors: Lrc, + can_reset: Lrc, + source_map: Option>, + ignore_list: Option, + ) -> SilentOnIgnoredFilesEmitter { + let emitter_writer = TestEmitter { num_emitted_errors }; + let source_map = + source_map.unwrap_or_else(|| Lrc::new(SourceMap::new(FilePathMapping::empty()))); + let ignore_path_set = Lrc::new( + IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap(), + ); + SilentOnIgnoredFilesEmitter { + has_non_ignorable_parser_errors: false, + source_map, + emitter: Box::new(emitter_writer), + ignore_path_set, + can_reset, + } + } + + fn get_ignore_list(config: &str) -> IgnoreList { + Config::from_toml(config, Path::new("")).unwrap().ignore() + } + + #[test] + fn handles_fatal_parse_error_in_ignored_file() { + let num_emitted_errors = Lrc::new(AtomicU32::new(0)); + let can_reset_errors = Lrc::new(AtomicBool::new(false)); + let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#); + let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let source = + String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#); + source_map.new_source_file( + SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))), + source, + ); + let mut emitter = build_emitter( + Lrc::clone(&num_emitted_errors), + Lrc::clone(&can_reset_errors), + Some(Lrc::clone(&source_map)), + Some(ignore_list), + ); + let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); + let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span)); + emitter.emit_diagnostic(&fatal_diagnostic); + assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1); + assert_eq!(can_reset_errors.load(Ordering::Acquire), false); + } + + #[nightly_only_test] + #[test] + fn handles_recoverable_parse_error_in_ignored_file() { + let num_emitted_errors = Lrc::new(AtomicU32::new(0)); + let can_reset_errors = Lrc::new(AtomicBool::new(false)); + let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#); + let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let source = String::from(r#"pub fn bar() { 1x; }"#); + source_map.new_source_file( + SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))), + source, + ); + let mut emitter = build_emitter( + Lrc::clone(&num_emitted_errors), + Lrc::clone(&can_reset_errors), + Some(Lrc::clone(&source_map)), + Some(ignore_list), + ); + let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); + let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span)); + emitter.emit_diagnostic(&non_fatal_diagnostic); + assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0); + assert_eq!(can_reset_errors.load(Ordering::Acquire), true); + } + + #[nightly_only_test] + #[test] + fn handles_recoverable_parse_error_in_non_ignored_file() { + let num_emitted_errors = Lrc::new(AtomicU32::new(0)); + let can_reset_errors = Lrc::new(AtomicBool::new(false)); + let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let source = String::from(r#"pub fn bar() { 1x; }"#); + source_map.new_source_file( + SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))), + source, + ); + let mut emitter = build_emitter( + Lrc::clone(&num_emitted_errors), + Lrc::clone(&can_reset_errors), + Some(Lrc::clone(&source_map)), + None, + ); + let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); + let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span)); + emitter.emit_diagnostic(&non_fatal_diagnostic); + assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1); + assert_eq!(can_reset_errors.load(Ordering::Acquire), false); + } + + #[nightly_only_test] + #[test] + fn handles_mix_of_recoverable_parse_error() { + let num_emitted_errors = Lrc::new(AtomicU32::new(0)); + let can_reset_errors = Lrc::new(AtomicBool::new(false)); + let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#); + let bar_source = String::from(r#"pub fn bar() { 1x; }"#); + let foo_source = String::from(r#"pub fn foo() { 1x; }"#); + let fatal_source = + String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#); + source_map.new_source_file( + SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("bar.rs"))), + bar_source, + ); + source_map.new_source_file( + SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))), + foo_source, + ); + source_map.new_source_file( + SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("fatal.rs"))), + fatal_source, + ); + let mut emitter = build_emitter( + Lrc::clone(&num_emitted_errors), + Lrc::clone(&can_reset_errors), + Some(Lrc::clone(&source_map)), + Some(ignore_list), + ); + let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); + let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22))); + let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span)); + let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span)); + let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None); + emitter.emit_diagnostic(&bar_diagnostic); + emitter.emit_diagnostic(&foo_diagnostic); + emitter.emit_diagnostic(&fatal_diagnostic); + assert_eq!(num_emitted_errors.load(Ordering::Acquire), 2); + assert_eq!(can_reset_errors.load(Ordering::Acquire), false); + } + } +} diff --git a/src/patterns.rs b/src/patterns.rs index a80d63201f9..9b74b35f314 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 c8abe70141b..4a3bd129d16 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 a394ce07398..1724a0f87bf 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 853336004d8..56d4ab40038 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/syntux/parser.rs b/src/syntux/parser.rs deleted file mode 100644 index 23d065c9cc9..00000000000 --- a/src/syntux/parser.rs +++ /dev/null @@ -1,258 +0,0 @@ -use std::panic::{catch_unwind, AssertUnwindSafe}; -use std::path::{Path, PathBuf}; - -use rustc_ast::token::{DelimToken, 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 crate::attr::first_attr_value_str_by_name; -use crate::syntux::session::ParseSess; -use crate::Input; - -pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership; -pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess; -pub(crate) type ModError<'a> = rustc_expand::module::ModError<'a>; - -#[derive(Clone)] -pub(crate) struct Directory { - pub(crate) path: PathBuf, - pub(crate) ownership: DirectoryOwnership, -} - -/// A parser for Rust source code. -pub(crate) struct Parser<'a> { - parser: RawParser<'a>, -} - -/// A builder for the `Parser`. -#[derive(Default)] -pub(crate) struct ParserBuilder<'a> { - sess: Option<&'a ParseSess>, - input: Option, -} - -impl<'a> ParserBuilder<'a> { - pub(crate) fn input(mut self, input: Input) -> ParserBuilder<'a> { - self.input = Some(input); - self - } - - pub(crate) fn sess(mut self, sess: &'a ParseSess) -> ParserBuilder<'a> { - self.sess = Some(sess); - self - } - - pub(crate) fn build(self) -> Result, ParserError> { - let sess = self.sess.ok_or(ParserError::NoParseSess)?; - let input = self.input.ok_or(ParserError::NoInput)?; - - let parser = match Self::parser(sess.inner(), input) { - Ok(p) => p, - Err(db) => { - if let Some(diagnostics) = db { - sess.emit_diagnostics(diagnostics); - return Err(ParserError::ParserCreationError); - } - return Err(ParserError::ParsePanicError); - } - }; - - Ok(Parser { parser }) - } - - fn parser( - sess: &'a rustc_session::parse::ParseSess, - input: Input, - ) -> Result, Option>> { - match input { - Input::File(ref file) => catch_unwind(AssertUnwindSafe(move || { - new_parser_from_file(sess, file, None) - })) - .map_err(|_| None), - Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str( - sess, - rustc_span::FileName::Custom("stdin".to_owned()), - text, - ) - .map_err(Some), - } - } -} - -#[derive(Debug, PartialEq)] -pub(crate) enum ParserError { - NoParseSess, - NoInput, - ParserCreationError, - ParseError, - ParsePanicError, -} - -impl<'a> Parser<'a> { - pub(crate) fn submod_path_from_attr(attrs: &[ast::Attribute], path: &Path) -> Option { - let path_sym = first_attr_value_str_by_name(attrs, sym::path)?; - let path_str = path_sym.as_str(); - - // On windows, the base path might have the form - // `\\?\foo\bar` in which case it does not tolerate - // mixed `/` and `\` separators, so canonicalize - // `/` to `\`. - #[cfg(windows)] - let path_str = path_str.replace("/", "\\"); - - Some(path.join(path_str)) - } - - pub(crate) fn parse_file_as_module( - sess: &'a ParseSess, - path: &Path, - span: Span, - ) -> Result<(Vec, Vec>, Span), ParserError> { - 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), - Err(mut e) => { - sess.emit_or_cancel_diagnostic(&mut e); - if sess.can_reset_errors() { - sess.reset_errors(); - } - None - } - } - })); - match result { - Ok(Some(m)) if !sess.has_errors() => Ok(m), - Ok(Some(m)) if sess.can_reset_errors() => { - sess.reset_errors(); - Ok(m) - } - Ok(_) => Err(ParserError::ParseError), - Err(..) if path.exists() => Err(ParserError::ParseError), - Err(_) => Err(ParserError::ParsePanicError), - } - } - - pub(crate) fn parse_crate( - input: Input, - sess: &'a ParseSess, - ) -> Result { - let krate = Parser::parse_crate_inner(input, sess)?; - if !sess.has_errors() { - return Ok(krate); - } - - if sess.can_reset_errors() { - sess.reset_errors(); - return Ok(krate); - } - - Err(ParserError::ParseError) - } - - fn parse_crate_inner(input: Input, sess: &'a ParseSess) -> Result { - ParserBuilder::default() - .input(input) - .sess(sess) - .build()? - .parse_crate_mod() - } - - fn parse_crate_mod(&mut self) -> Result { - let mut parser = AssertUnwindSafe(&mut self.parser); - - match catch_unwind(move || parser.parse_crate_mod()) { - Ok(Ok(k)) => Ok(k), - Ok(Err(mut db)) => { - db.emit(); - Err(ParserError::ParseError) - } - 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/syntux/session.rs deleted file mode 100644 index dd7c7352686..00000000000 --- a/src/syntux/session.rs +++ /dev/null @@ -1,465 +0,0 @@ -use std::path::Path; -use std::sync::atomic::{AtomicBool, Ordering}; - -use rustc_data_structures::sync::{Lrc, Send}; -use rustc_errors::emitter::{Emitter, EmitterWriter}; -use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel}; -use rustc_session::parse::ParseSess as RawParseSess; -use rustc_span::{ - source_map::{FilePathMapping, SourceMap}, - symbol, BytePos, Span, -}; - -use crate::config::file_lines::LineRange; -use crate::ignore_path::IgnorePathSet; -use crate::source_map::LineRangeUtils; -use crate::utils::starts_with_newline; -use crate::visitor::SnippetProvider; -use crate::{Config, ErrorKind, FileName}; - -/// ParseSess holds structs necessary for constructing a parser. -pub(crate) struct ParseSess { - parse_sess: RawParseSess, - ignore_path_set: Lrc, - can_reset_errors: Lrc, -} - -/// Emitter which discards every error. -struct SilentEmitter; - -impl Emitter for SilentEmitter { - fn source_map(&self) -> Option<&Lrc> { - None - } - fn emit_diagnostic(&mut self, _db: &Diagnostic) {} -} - -fn silent_emitter() -> Box { - Box::new(SilentEmitter {}) -} - -/// Emit errors against every files expect ones specified in the `ignore_path_set`. -struct SilentOnIgnoredFilesEmitter { - ignore_path_set: Lrc, - source_map: Lrc, - emitter: Box, - has_non_ignorable_parser_errors: bool, - can_reset: Lrc, -} - -impl SilentOnIgnoredFilesEmitter { - fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) { - self.has_non_ignorable_parser_errors = true; - self.can_reset.store(false, Ordering::Release); - self.emitter.emit_diagnostic(db); - } -} - -impl Emitter for SilentOnIgnoredFilesEmitter { - fn source_map(&self) -> Option<&Lrc> { - None - } - fn emit_diagnostic(&mut self, db: &Diagnostic) { - if db.level == DiagnosticLevel::Fatal { - return self.handle_non_ignoreable_error(db); - } - if let Some(primary_span) = &db.span.primary_span() { - let file_name = self.source_map.span_to_filename(*primary_span); - if let rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(ref path)) = - file_name - { - if self - .ignore_path_set - .is_match(&FileName::Real(path.to_path_buf())) - { - if !self.has_non_ignorable_parser_errors { - self.can_reset.store(true, Ordering::Release); - } - return; - } - }; - } - self.handle_non_ignoreable_error(db); - } -} - -fn default_handler( - source_map: Lrc, - ignore_path_set: Lrc, - can_reset: Lrc, - hide_parse_errors: bool, -) -> Handler { - let supports_color = term::stderr().map_or(false, |term| term.supports_color()); - let color_cfg = if supports_color { - ColorConfig::Auto - } else { - ColorConfig::Never - }; - - let emitter = if hide_parse_errors { - silent_emitter() - } else { - Box::new(EmitterWriter::stderr( - color_cfg, - Some(source_map.clone()), - false, - false, - None, - false, - )) - }; - Handler::with_emitter( - true, - None, - Box::new(SilentOnIgnoredFilesEmitter { - has_non_ignorable_parser_errors: false, - source_map, - emitter, - ignore_path_set, - can_reset, - }), - ) -} - -impl ParseSess { - pub(crate) fn new(config: &Config) -> Result { - let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) { - Ok(ignore_path_set) => Lrc::new(ignore_path_set), - Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)), - }; - let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let can_reset_errors = Lrc::new(AtomicBool::new(false)); - - let handler = default_handler( - Lrc::clone(&source_map), - Lrc::clone(&ignore_path_set), - Lrc::clone(&can_reset_errors), - config.hide_parse_errors(), - ); - let parse_sess = RawParseSess::with_span_handler(handler, source_map); - - Ok(ParseSess { - parse_sess, - ignore_path_set, - can_reset_errors, - }) - } - - 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) - } - - pub(crate) fn is_file_parsed(&self, path: &Path) -> bool { - self.parse_sess - .source_map() - .get_source_file(&rustc_span::FileName::Real( - rustc_span::RealFileName::LocalPath(path.to_path_buf()), - )) - .is_some() - } - - pub(crate) fn ignore_file(&self, path: &FileName) -> bool { - self.ignore_path_set.as_ref().is_match(path) - } - - pub(crate) fn set_silent_emitter(&mut self) { - self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter()); - } - - pub(crate) fn span_to_filename(&self, span: Span) -> FileName { - self.parse_sess.source_map().span_to_filename(span).into() - } - - pub(crate) fn span_to_file_contents(&self, span: Span) -> Lrc { - self.parse_sess - .source_map() - .lookup_source_file(span.data().lo) - } - - pub(crate) fn span_to_first_line_string(&self, span: Span) -> String { - let file_lines = self.parse_sess.source_map().span_to_lines(span).ok(); - - match file_lines { - Some(fl) => fl - .file - .get_line(fl.lines[0].line_index) - .map_or_else(String::new, |s| s.to_string()), - None => String::new(), - } - } - - pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize { - self.parse_sess.source_map().lookup_char_pos(pos).line - } - - pub(crate) fn span_to_debug_info(&self, span: Span) -> String { - self.parse_sess.source_map().span_to_diagnostic_string(span) - } - - pub(crate) fn inner(&self) -> &RawParseSess { - &self.parse_sess - } - - pub(crate) fn snippet_provider(&self, span: Span) -> SnippetProvider { - let source_file = self.parse_sess.source_map().lookup_char_pos(span.lo()).file; - SnippetProvider::new( - source_file.start_pos, - source_file.end_pos, - Lrc::clone(source_file.src.as_ref().unwrap()), - ) - } - - pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option> { - self.parse_sess - .source_map() - .get_source_file(&file_name.into()) - .and_then(|source_file| source_file.src.clone()) - } -} - -// Methods that should be restricted within the syntux module. -impl ParseSess { - pub(super) fn emit_diagnostics(&self, diagnostics: Vec) { - for diagnostic in diagnostics { - self.parse_sess.span_diagnostic.emit_diagnostic(&diagnostic); - } - } - - 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) - } - - pub(super) fn has_errors(&self) -> bool { - self.parse_sess.span_diagnostic.has_errors() - } - - pub(super) fn reset_errors(&self) { - self.parse_sess.span_diagnostic.reset_err_count(); - } -} - -impl LineRangeUtils for ParseSess { - fn lookup_line_range(&self, span: Span) -> LineRange { - let snippet = self - .parse_sess - .source_map() - .span_to_snippet(span) - .unwrap_or_default(); - let lo = self.parse_sess.source_map().lookup_line(span.lo()).unwrap(); - let hi = self.parse_sess.source_map().lookup_line(span.hi()).unwrap(); - - debug_assert_eq!( - lo.sf.name, hi.sf.name, - "span crossed file boundary: lo: {:?}, hi: {:?}", - lo, hi - ); - - // in case the span starts with a newline, the line range is off by 1 without the - // adjustment below - let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 }; - // Line numbers start at 1 - LineRange { - file: lo.sf.clone(), - lo: lo.line + offset, - hi: hi.line + offset, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use rustfmt_config_proc_macro::nightly_only_test; - - mod emitter { - use super::*; - use crate::config::IgnoreList; - use crate::utils::mk_sp; - use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName, DUMMY_SP}; - use std::path::PathBuf; - use std::sync::atomic::AtomicU32; - - struct TestEmitter { - num_emitted_errors: Lrc, - } - - impl Emitter for TestEmitter { - fn source_map(&self) -> Option<&Lrc> { - None - } - fn emit_diagnostic(&mut self, _db: &Diagnostic) { - self.num_emitted_errors.fetch_add(1, Ordering::Release); - } - } - - fn build_diagnostic(level: DiagnosticLevel, span: Option) -> Diagnostic { - Diagnostic { - level, - code: None, - message: vec![], - children: vec![], - suggestions: vec![], - span: span.unwrap_or_else(MultiSpan::new), - sort_span: DUMMY_SP, - is_lint: false, - } - } - - fn build_emitter( - num_emitted_errors: Lrc, - can_reset: Lrc, - source_map: Option>, - ignore_list: Option, - ) -> SilentOnIgnoredFilesEmitter { - let emitter_writer = TestEmitter { num_emitted_errors }; - let source_map = - source_map.unwrap_or_else(|| Lrc::new(SourceMap::new(FilePathMapping::empty()))); - let ignore_path_set = Lrc::new( - IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap(), - ); - SilentOnIgnoredFilesEmitter { - has_non_ignorable_parser_errors: false, - source_map, - emitter: Box::new(emitter_writer), - ignore_path_set, - can_reset, - } - } - - fn get_ignore_list(config: &str) -> IgnoreList { - Config::from_toml(config, Path::new("")).unwrap().ignore() - } - - #[test] - fn handles_fatal_parse_error_in_ignored_file() { - let num_emitted_errors = Lrc::new(AtomicU32::new(0)); - let can_reset_errors = Lrc::new(AtomicBool::new(false)); - let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#); - let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let source = - String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#); - source_map.new_source_file( - SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))), - source, - ); - let mut emitter = build_emitter( - Lrc::clone(&num_emitted_errors), - Lrc::clone(&can_reset_errors), - Some(Lrc::clone(&source_map)), - Some(ignore_list), - ); - let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); - let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span)); - emitter.emit_diagnostic(&fatal_diagnostic); - assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1); - assert_eq!(can_reset_errors.load(Ordering::Acquire), false); - } - - #[nightly_only_test] - #[test] - fn handles_recoverable_parse_error_in_ignored_file() { - let num_emitted_errors = Lrc::new(AtomicU32::new(0)); - let can_reset_errors = Lrc::new(AtomicBool::new(false)); - let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#); - let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let source = String::from(r#"pub fn bar() { 1x; }"#); - source_map.new_source_file( - SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))), - source, - ); - let mut emitter = build_emitter( - Lrc::clone(&num_emitted_errors), - Lrc::clone(&can_reset_errors), - Some(Lrc::clone(&source_map)), - Some(ignore_list), - ); - let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); - let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span)); - emitter.emit_diagnostic(&non_fatal_diagnostic); - assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0); - assert_eq!(can_reset_errors.load(Ordering::Acquire), true); - } - - #[nightly_only_test] - #[test] - fn handles_recoverable_parse_error_in_non_ignored_file() { - let num_emitted_errors = Lrc::new(AtomicU32::new(0)); - let can_reset_errors = Lrc::new(AtomicBool::new(false)); - let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let source = String::from(r#"pub fn bar() { 1x; }"#); - source_map.new_source_file( - SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))), - source, - ); - let mut emitter = build_emitter( - Lrc::clone(&num_emitted_errors), - Lrc::clone(&can_reset_errors), - Some(Lrc::clone(&source_map)), - None, - ); - let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); - let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span)); - emitter.emit_diagnostic(&non_fatal_diagnostic); - assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1); - assert_eq!(can_reset_errors.load(Ordering::Acquire), false); - } - - #[nightly_only_test] - #[test] - fn handles_mix_of_recoverable_parse_error() { - let num_emitted_errors = Lrc::new(AtomicU32::new(0)); - let can_reset_errors = Lrc::new(AtomicBool::new(false)); - let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#); - let bar_source = String::from(r#"pub fn bar() { 1x; }"#); - let foo_source = String::from(r#"pub fn foo() { 1x; }"#); - let fatal_source = - String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#); - source_map.new_source_file( - SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("bar.rs"))), - bar_source, - ); - source_map.new_source_file( - SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))), - foo_source, - ); - source_map.new_source_file( - SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("fatal.rs"))), - fatal_source, - ); - let mut emitter = build_emitter( - Lrc::clone(&num_emitted_errors), - Lrc::clone(&can_reset_errors), - Some(Lrc::clone(&source_map)), - Some(ignore_list), - ); - let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); - let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22))); - let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span)); - let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span)); - let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None); - emitter.emit_diagnostic(&bar_diagnostic); - emitter.emit_diagnostic(&foo_diagnostic); - emitter.emit_diagnostic(&fatal_diagnostic); - assert_eq!(num_emitted_errors.load(Ordering::Acquire), 2); - assert_eq!(can_reset_errors.load(Ordering::Acquire), false); - } - } -} diff --git a/src/test/configuration_snippet.rs b/src/test/configuration_snippet.rs index ef7dd0ddcd1..92949ab576a 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 ae4a0d0fccb..ec9ed0f0b8d 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 e4a7be742ab..0177689958a 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) { -- cgit 1.4.1-3-g733a5 From 737e6f704671dbf30e81dababa0133b585c03b48 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 19 Dec 2021 21:27:25 -0500 Subject: 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 ++++++++ .../test-submodule-issue-5119/tests/test1/sub1.rs | 6 ++++++ .../test-submodule-issue-5119/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 (limited to 'src') diff --git a/src/modules.rs b/src/modules.rs index 9c964b274e0..70b937b0283 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 ec9ed0f0b8d..fcff6d14e6f 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 5493b09e4aa..bf81f253f69 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 00000000000..0993f127959 --- /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 00000000000..3f7ddba8a28 --- /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 00000000000..da4e86169ad --- /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 00000000000..b760ba23cd2 --- /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 00000000000..4fd8286eac4 --- /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 00000000000..e029785bc24 --- /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 00000000000..e69de29bb2d -- cgit 1.4.1-3-g733a5 From 93b7de5b0110a1ab79714c3dfd009a4aae3e34b1 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Wed, 6 Nov 2019 10:15:34 +0000 Subject: 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 (limited to 'src') diff --git a/src/bin/main.rs b/src/bin/main.rs index 4d845547cdf..6f5b09fc86a 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 76f2527db3d..545b259979d 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 269dd2d4daf..4d6f972c5e3 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 cceb28dfea6..e1a7972ec82 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 00000000000..06f8a0c288d --- /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 00000000000..20e38f57f4a --- /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 00000000000..e70708338f5 --- /dev/null +++ b/tests/writemode/target/stdin.xml @@ -0,0 +1,2 @@ + + -- cgit 1.4.1-3-g733a5 From 34263cd6bdb29df23258a96ea18fe986efe8dad0 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Tue, 12 Nov 2019 02:55:04 +0000 Subject: 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(-) (limited to 'src') diff --git a/src/emitter/diff.rs b/src/emitter/diff.rs index 7264ad8bbf3..5e1f1344656 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 e1a7972ec82..db1cf88479c 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"); +} -- cgit 1.4.1-3-g733a5 From 776baf93f8fd1e2f3a66da4c2a1dd92114cef9c6 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 9 Dec 2019 04:41:16 -0600 Subject: 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(-) (limited to 'src') diff --git a/src/emitter/json.rs b/src/emitter/json.rs index 4d6f972c5e3..7c0f862cbc6 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 b5f327b0a1c..acb33dea7ef 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 20e38f57f4a..ae6796863e5 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() {}"}]}] -- cgit 1.4.1-3-g733a5 From 894a3c0e7745ece39fcee89d38389a789cfe520a Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 23 Jun 2020 01:20:29 +0100 Subject: 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(-) (limited to 'src') diff --git a/src/emitter/json.rs b/src/emitter/json.rs index 7c0f862cbc6..c7f68d4675a 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 acb33dea7ef..d8b5467ee91 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 ae6796863e5..6f5d5bfb8ca 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"}]}] -- cgit 1.4.1-3-g733a5 From 34d374ee5d23951d166ca3f51d477ddf6526a2fa Mon Sep 17 00:00:00 2001 From: Seiichi Uchida Date: Fri, 3 Jul 2020 11:13:16 +0900 Subject: 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(-) (limited to 'src') diff --git a/src/config/file_lines.rs b/src/config/file_lines.rs index 7b498dc46b3..e4e51a3f3b4 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 db1cf88479c..2d5a8f22053 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 6f5d5bfb8ca..dbf2c486322 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 e70708338f5..a7301bbc553 100644 --- a/tests/writemode/target/stdin.xml +++ b/tests/writemode/target/stdin.xml @@ -1,2 +1,2 @@ - - + + -- cgit 1.4.1-3-g733a5 From 5056f4cfb311a084420f1828cd58af94d143f5e0 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Wed, 5 Jan 2022 21:19:36 +0100 Subject: 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(-) (limited to 'src') diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index 759b21218c3..8cb7b4585ec 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 c820259256c..90406cdb95e 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 f29552caf8d..fdbe3374615 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 { -- cgit 1.4.1-3-g733a5 From 6e6300207f835c0a6c4dd0b6a4e55ab07d3d272b Mon Sep 17 00:00:00 2001 From: Lamb Date: Mon, 26 Jul 2021 05:38:16 +0200 Subject: 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(+) (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs index ad23b16e02e..fae8080c02e 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; -- cgit 1.4.1-3-g733a5 From 04670a14046b96a17bee2c2b36c3c2002ce3b0b6 Mon Sep 17 00:00:00 2001 From: Tomasz MiÄ…sko Date: Wed, 12 Jan 2022 00:00:00 +0000 Subject: Remove LLVM-style inline assembly from rustfmt --- src/expr.rs | 4 +--- src/utils.rs | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'src') diff --git a/src/expr.rs b/src/expr.rs index c9c8852cd3b..e1865c8afc2 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 0c0b789a6ef..2428d8cb0fd 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(..) -- cgit 1.4.1-3-g733a5 From bfbf42cecbed85406b3b83482466da39ab5b4551 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Fri, 14 Jan 2022 18:18:37 -0600 Subject: 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(-) (limited to 'src') diff --git a/Configurations.md b/Configurations.md index a89fbe863e6..4476f2a449b 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 5dbe532ac38..cd90e0904b6 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 67cf1232f66..ca93955a549 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 cceb28dfea6..c50d18644b0 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(); -- cgit 1.4.1-3-g733a5 From f5ce84e4f25304dfbf9efeb26a85ef3c9ad594ad Mon Sep 17 00:00:00 2001 From: kadmin Date: Fri, 30 Jul 2021 08:56:45 +0000 Subject: add eq constraints on associated constants --- src/types.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/types.rs b/src/types.rs index 88f5dc43245..4bad9742a0e 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), } } } -- cgit 1.4.1-3-g733a5 From cf86d532029bb27ef96780015a50613d2130102b Mon Sep 17 00:00:00 2001 From: kadmin Date: Fri, 7 Jan 2022 03:58:32 +0000 Subject: 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(-) (limited to 'src') diff --git a/src/types.rs b/src/types.rs index 4bad9742a0e..9d5f790e809 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), } } -- cgit 1.4.1-3-g733a5 From 7913f130d3ee0682916586c7b43664b651435594 Mon Sep 17 00:00:00 2001 From: kadmin Date: Thu, 13 Jan 2022 07:39:58 +0000 Subject: 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(-) (limited to 'src') diff --git a/src/types.rs b/src/types.rs index 9d5f790e809..5de30129266 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), } -- cgit 1.4.1-3-g733a5 From 9e1973f1d9b94914c3e5539f3ed992c19389b4a7 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Sun, 23 Jan 2022 11:18:17 -0600 Subject: chore: bump toolchain, update test --- rust-toolchain | 2 +- src/test/mod.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/rust-toolchain b/rust-toolchain index d4cdcec2018..d8bf02aec85 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 c399512ba7e..ab966d4a360 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] -- cgit 1.4.1-3-g733a5 From 3572c542c29facef5073ba801213fb160c376b4a Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Mon, 24 Jan 2022 11:23:14 +0000 Subject: rustc_errors: only box the `diagnostic` field in `DiagnosticBuilder`. --- src/parse/session.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/parse/session.rs b/src/parse/session.rs index 624fed0d2de..7fc3778376c 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, -- cgit 1.4.1-3-g733a5 From b4a4bf0bf8a16913b9f16f2aee7030065ff00931 Mon Sep 17 00:00:00 2001 From: Szymon GibaÅ‚a Date: Sat, 29 Jan 2022 05:55:47 +0100 Subject: 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 +- .../target/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 (limited to 'src') diff --git a/src/items.rs b/src/items.rs index babc56f86ed..4c7a33c86d2 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 00000000000..35d6e21affe --- /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 be31bf0a331..be4b7a8c42e 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 80aea59d1b5..db4da622372 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 52315f470e4..9b9147eb124 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 e0bfcf0b500..c1531d22a4a 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 00000000000..a81c5a5171f --- /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 */} +} -- cgit 1.4.1-3-g733a5 From 8b0b213cddb23a9bbe421b717d1a0e5fb3982712 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Thu, 9 Dec 2021 23:17:43 -0500 Subject: 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 ++++++ .../issue-5125/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 (limited to 'src') diff --git a/src/items.rs b/src/items.rs index 4c7a33c86d2..007609aba24 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 00000000000..5d167932828 --- /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 00000000000..cab20381ce8 --- /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 00000000000..8003e66968c --- /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 00000000000..2340b2f3472 --- /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 +) { +} -- cgit 1.4.1-3-g733a5 From 368a9b7cef25c22c3e836453e73d8584b251b578 Mon Sep 17 00:00:00 2001 From: Stéphane Campinas Date: Wed, 2 Feb 2022 02:06:14 +0100 Subject: 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 (limited to 'src') diff --git a/src/string.rs b/src/string.rs index 64ae15672df..b65aa5b33b2 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 00000000000..ae1c723eff7 --- /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 00000000000..4e84c7d9842 --- /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(); + } +} -- cgit 1.4.1-3-g733a5 From 606894eb0b1dcc3a4616aff4b54dbd19a2a46ba5 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 19 Dec 2021 16:47:13 -0500 Subject: 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 +++++++++++++++++++ .../multi-line_comment_without_trailing_comma.rs | 24 +++++++++++++++++++ .../single-line_comment_with_trailing_comma.rs | 9 ++++++++ .../single-line_comment_without_trailing_comma.rs | 10 ++++++++ .../multi-line_comment_with_trailing_comma.rs | 24 +++++++++++++++++++ .../multi-line_comment_without_trailing_comma.rs | 24 +++++++++++++++++++ .../single-line_comment_with_trailing_comma.rs | 7 ++++++ .../single-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 (limited to 'src') diff --git a/src/lists.rs b/src/lists.rs index 7aa0315f18c..97eea19f932 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 00000000000..5d171f32a1a --- /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 00000000000..b8a824b34b7 --- /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 00000000000..bd765b7b41f --- /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 00000000000..2ed8de875ad --- /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 00000000000..1ae1212b488 --- /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 00000000000..30d174664c9 --- /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 00000000000..87b651dd285 --- /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 00000000000..116df86a4b5 --- /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 /* ... */); +} -- cgit 1.4.1-3-g733a5 From b2c7a52ea865f877bc0b57075fe1a88f83120546 Mon Sep 17 00:00:00 2001 From: Stéphane Campinas Date: Mon, 31 Jan 2022 23:45:30 +0100 Subject: 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 (limited to 'src') diff --git a/src/imports.rs b/src/imports.rs index 40e0d06f99d..c60bec6d4a2 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 00000000000..3e9139177c5 --- /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 00000000000..763024d6fa4 --- /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); + } + } +} -- cgit 1.4.1-3-g733a5 From 5df8c8f7e554e036fcb14a2d93d145c92d56bc2e Mon Sep 17 00:00:00 2001 From: Frank King Date: Mon, 7 Feb 2022 10:57:39 +0800 Subject: 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 (limited to 'src') diff --git a/src/spanned.rs b/src/spanned.rs index 8e6c75a3744..2136cfeae1a 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 5de30129266..b13e75a93b3 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 00000000000..2efc5e09a3d --- /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, +>; -- cgit 1.4.1-3-g733a5 From ace7241087bedd2ea26d7b1dfcdd6133cbf77ce7 Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sat, 29 Jan 2022 16:44:49 -0500 Subject: 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 (limited to 'src') diff --git a/CHANGELOG.md b/CHANGELOG.md index b59438dc4fe..1b9208d31e0 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 2428d8cb0fd..35512e78fa6 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 00000000000..9f9675f5163 --- /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 00000000000..e1865dd0868 --- /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 00000000000..fa54d2e3e09 --- /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 00000000000..54e490b7fbe --- /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 00000000000..394dc8633f5 --- /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 00000000000..01cafa76b68 --- /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.", + ) + } + }; +} -- cgit 1.4.1-3-g733a5 From b05b3138001c97cbabeb3e20a0960b9f75341ee0 Mon Sep 17 00:00:00 2001 From: Tharun Rajendran Date: Fri, 11 Feb 2022 10:05:45 +0530 Subject: 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(-) (limited to 'src') diff --git a/src/bin/main.rs b/src/bin/main.rs index 6f5b09fc86a..196de6056b5 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 8effb1c6fca..6976cc4d346 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] ..."))); +} -- cgit 1.4.1-3-g733a5 From 1e78a2b258470fcab13e8c933212667e64b21d3e Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Wed, 29 Dec 2021 19:59:54 -0500 Subject: 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 (limited to 'src') diff --git a/src/comment.rs b/src/comment.rs index 0f850b9b2f2..96778c4ef1d 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 00000000000..5c1d79a7430 --- /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 00000000000..cf200d04e08 --- /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 00000000000..eb436402e4e --- /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 00000000000..e47677f2039 --- /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 00000000000..079510442b7 --- /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 00000000000..029ee37d22a --- /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() {} -- cgit 1.4.1-3-g733a5 From 6c476127aedc37b43769469f87e0fbc382382739 Mon Sep 17 00:00:00 2001 From: Travis Finkenauer Date: Tue, 15 Feb 2022 15:25:44 -0800 Subject: 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(-) (limited to 'src') diff --git a/src/bin/main.rs b/src/bin/main.rs index 196de6056b5..ad10b9ede60 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 cd90e0904b6..5041e1e36dd 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)); } } _ => {} -- cgit 1.4.1-3-g733a5 From c63d42e80473a0c18714b55058f27506fd24955c Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 1 Feb 2022 13:39:01 -0600 Subject: Use cargo-fmt in self_tests --- src/test/mod.rs | 46 ++++++++++++---------------------------------- 1 file changed, 12 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/test/mod.rs b/src/test/mod.rs index ab966d4a360..4191e3e96b0 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] -- cgit 1.4.1-3-g733a5 From 7592663e85fe82de5f346a77a22772230b1378a1 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Sun, 23 Jan 2022 23:11:37 +0000 Subject: rustc_errors: add `downgrade_to_delayed_bug` to `Diagnostic` itself. --- src/parse/session.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/parse/session.rs b/src/parse/session.rs index 7fc3778376c..d26bb8c2025 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( -- cgit 1.4.1-3-g733a5 From 57239460813806d74f6e5404876a6f3fc8f9262b Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Wed, 26 Jan 2022 03:39:14 +0000 Subject: 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(-) (limited to 'src') diff --git a/src/modules.rs b/src/modules.rs index 9c964b274e0..d4bddd95785 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 e10fbe64bcd..306b6bb745e 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 9c8651aa3fa..4c541de04be 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 2e9ce1d35f4..fd738908170 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 657217633f4..f0944a88d2f 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 d26bb8c2025..40a6d708d8c 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) } -- cgit 1.4.1-3-g733a5 From 89ca3f3a100456d652b156b1a62b8e244e526c4e Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 23 Feb 2022 21:37:42 -0600 Subject: 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(-) (limited to 'src') diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index db497941642..45f63b83c05 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 7738eee0a76..d955949496a 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 fae8080c02e..ad23b16e02e 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; -- cgit 1.4.1-3-g733a5 From de1ac375f079a9bf7f2aa81c47fa46ecdb32d074 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Wed, 23 Feb 2022 08:11:17 -0500 Subject: Enable rustc_pass_by_value for Span --- src/expr.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/expr.rs b/src/expr.rs index e1865c8afc2..4f333cd27ce 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(), -- cgit 1.4.1-3-g733a5 From 12048e444f8a68b3c13e98b15c11a69de9f0b485 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 30 Jan 2022 13:46:53 -0500 Subject: 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 + tests/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 +++ tests/mod-resolver/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 (limited to 'src') diff --git a/src/parse/session.rs b/src/parse/session.rs index 624fed0d2de..fb9182152d1 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 fcff6d14e6f..aacb2acc684 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 00000000000..cd686f56116 --- /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 00000000000..696832913c8 --- /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 00000000000..cd686f56116 --- /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 00000000000..d1604aa23a3 --- /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 00000000000..92c9e302143 --- /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 00000000000..cd686f56116 --- /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 00000000000..cd686f56116 --- /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 00000000000..cd686f56116 --- /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 00000000000..81904619650 --- /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 00000000000..d436a8076cd --- /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. -- cgit 1.4.1-3-g733a5 From 272fb42f06479afb62a9503f181540101f67982a Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Fri, 25 Feb 2022 11:31:09 -0500 Subject: 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 ++++++++++++++---- .../issue-5238/markdown_header_wrap_comments_false.rs | 11 +++++++++++ .../issue-5238/markdown_header_wrap_comments_true.rs | 11 +++++++++++ .../issue-5238/markdown_header_wrap_comments_false.rs | 11 +++++++++++ .../issue-5238/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 (limited to 'src') diff --git a/src/comment.rs b/src/comment.rs index 96778c4ef1d..f9d8a0fa70c 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 00000000000..229c6e5753d --- /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 00000000000..c547ff35c69 --- /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 00000000000..229c6e5753d --- /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 00000000000..87dae58eccd --- /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` +} -- cgit 1.4.1-3-g733a5 From 4edb7578261ae0cc547c4346f8c0a82e124ac524 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 20 Jan 2022 11:06:45 -0500 Subject: 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(-) (limited to 'src') diff --git a/src/parse/parser.rs b/src/parse/parser.rs index f0944a88d2f..3b4e762b6dd 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 0177689958a..57a58c60484 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); -- cgit 1.4.1-3-g733a5 From 74876ef4e9b29184787f6d8f3ba447e78def3a47 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 3 Mar 2022 18:45:25 -0500 Subject: Associate multiple with a crate too. --- src/modules.rs | 4 ++-- src/visitor.rs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modules.rs b/src/modules.rs index d4bddd95785..64d96a5c6a6 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 57a58c60484..c44b2fc6ae3 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); -- cgit 1.4.1-3-g733a5 From 651f46376aa6bb259c58cbb3debad6e0edce31bf Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 20 Jan 2022 14:07:54 -0500 Subject: 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(-) (limited to 'src') diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 3b4e762b6dd..6983249c15d 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 c44b2fc6ae3..dec977e98ca 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); -- cgit 1.4.1-3-g733a5 From ce301d92f12658edf6fe410d39de445270d1420e Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 4 Mar 2022 17:05:30 -0500 Subject: Placate tidy in submodule. --- src/parse/parser.rs | 8 +++++++- src/visitor.rs | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 6983249c15d..ec051d93710 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 dec977e98ca..dcf096294f1 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); -- cgit 1.4.1-3-g733a5 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: Change syntax for TyAlias where clauses --- src/items.rs | 108 ++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 74 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/items.rs b/src/items.rs index babc56f86ed..8498cb6adda 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, -- cgit 1.4.1-3-g733a5 From ee130515e3788d886cdf18442871936a99c134b6 Mon Sep 17 00:00:00 2001 From: cassaundra Date: Wed, 29 Dec 2021 15:41:26 -0800 Subject: 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 (limited to 'src') diff --git a/src/vertical.rs b/src/vertical.rs index c4208848c6c..a06bc995aa5 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 00000000000..4760022eeaf --- /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 00000000000..fff58be99a5 --- /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 +} -- cgit 1.4.1-3-g733a5 From 003eaf8fe270cb8f6915ed03ad23f4af0212d607 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 7 Mar 2022 16:37:35 -0500 Subject: placate rustfmt in rustfmt. --- src/parse/parser.rs | 8 +------- src/visitor.rs | 4 ++-- 2 files changed, 3 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/parse/parser.rs b/src/parse/parser.rs index ec051d93710..268c72649a6 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 dcf096294f1..3ebfa551d1c 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 => { -- cgit 1.4.1-3-g733a5 From 18c0369688d5854ea67dbe29cfac3cc8380cb20e Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 10 Jan 2022 20:45:27 -0500 Subject: 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 + .../module-not-found/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 (limited to 'src') diff --git a/src/modules.rs b/src/modules.rs index 70b937b0283..49c99403974 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 fb9182152d1..87ab8fbf20a 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 00000000000..e69de29bb2d 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 00000000000..e69de29bb2d 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 00000000000..f21af614da0 --- /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 00000000000..2a63c961be8 --- /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 00000000000..4a1eac8965d --- /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 00000000000..f21af614da0 --- /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 00000000000..d9d9e1e3c90 --- /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 2262ae3aaac..450051d2fec 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")); +} -- cgit 1.4.1-3-g733a5 From b4de150dbc65a6bb95c86517430726c55251d0b0 Mon Sep 17 00:00:00 2001 From: Tom Milligan Date: Fri, 4 Mar 2022 18:13:08 +0000 Subject: 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(-) (limited to 'src') diff --git a/src/imports.rs b/src/imports.rs index c60bec6d4a2..02319809486 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 5a4fad5872b..2d7bb299aaa 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 9c1387c466a..e4e1a299e58 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)] -- cgit 1.4.1-3-g733a5 From 5696e3859707d2abf12465e7bfbdcf2d9f42c8a2 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 12 Mar 2022 01:16:08 -0500 Subject: 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(+) (limited to 'src') diff --git a/src/test/configuration_snippet.rs b/src/test/configuration_snippet.rs index 92949ab576a..c8fda7c8556 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, + ); + } + } +} -- cgit 1.4.1-3-g733a5 From 1bb85bdf6b6dc0e205009b9f531a6220fe2031f9 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Wed, 9 Mar 2022 20:53:51 -0600 Subject: chore: add utility function for relative span positions --- src/parse/session.rs | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/parse/session.rs b/src/parse/session.rs index 87ab8fbf20a..a95324bbb0e 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) } -- cgit 1.4.1-3-g733a5 From c0861d3a9d33d535542bbeb48d35271161c87ebf Mon Sep 17 00:00:00 2001 From: codehorseman Date: Wed, 16 Mar 2022 20:12:30 +0800 Subject: 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(-) (limited to 'src') diff --git a/src/lists.rs b/src/lists.rs index 7aa0315f18c..29d75585eb7 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 5de30129266..a49d473a13f 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 -- cgit 1.4.1-3-g733a5 From c1d351f6eee7f870ce5216e824709dccbdf69dff Mon Sep 17 00:00:00 2001 From: mark Date: Sat, 22 Jan 2022 18:49:12 -0600 Subject: 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(-) (limited to 'src') diff --git a/src/parse/macros/lazy_static.rs b/src/parse/macros/lazy_static.rs index 4c541de04be..a8c2feec453 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 fd738908170..3728f3a19b4 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 40a6d708d8c..a34ceed3fc9 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) { -- cgit 1.4.1-3-g733a5 From 4f89c51a5b3f27f38a7701d5243a80d7c1c44dff Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 20 Mar 2022 18:26:09 +0100 Subject: 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(-) (limited to 'src') diff --git a/src/parse/session.rs b/src/parse/session.rs index a34ceed3fc9..412f4434b9e 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); } } -- cgit 1.4.1-3-g733a5 From e41329ce87409df929ecfa191297f944472cc999 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 20 Mar 2022 13:21:44 -0400 Subject: 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 (limited to 'src') diff --git a/src/items.rs b/src/items.rs index 9b35d28f119..92f423bbb62 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 00000000000..3bb9048a5fd --- /dev/null +++ b/tests/target/issue_5273.rs @@ -0,0 +1,3 @@ +struct Example { + // +} -- cgit 1.4.1-3-g733a5 From 0dba01aee15a45b8417c06df69e38af6956e03a8 Mon Sep 17 00:00:00 2001 From: 123vivekr <123vivekr@gmail.com> Date: Sun, 13 Feb 2022 17:54:09 +0530 Subject: 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 (limited to 'src') diff --git a/Configurations.md b/Configurations.md index ecec97dca52..a47439b9ba9 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 5041e1e36dd..18e1854612b 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 3475f5c378c..80aed998d73 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 00000000000..7d0d70919a6 --- /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 00000000000..8a93a51d6a2 --- /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 00000000000..710b6fe7c4b --- /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 00000000000..78c4adba1c1 --- /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 00000000000..6084690652f --- /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 00000000000..710b6fe7c4b --- /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, + ]; +} -- cgit 1.4.1-3-g733a5 From 8984438a6faf11e0cb8e876e80f177a42a43904d Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Fri, 18 Mar 2022 09:48:45 -0400 Subject: 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 ++++++++++++++++++++++++++ tests/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 (limited to 'src') diff --git a/src/attr.rs b/src/attr.rs index 3887a8051f2..befe12ae2c4 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 00000000000..b31bbf095e7 --- /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 00000000000..3b6f7e66993 --- /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 00000000000..5f488b4542d --- /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, + } +} -- cgit 1.4.1-3-g733a5 From 4fecede7fdaeeb859b066394bf27401fd13743a7 Mon Sep 17 00:00:00 2001 From: Caleb Cartwright Date: Mon, 28 Mar 2022 20:27:42 -0500 Subject: Revert "Use cargo-fmt in self_tests" This reverts commit c63d42e80473a0c18714b55058f27506fd24955c. --- src/test/mod.rs | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/test/mod.rs b/src/test/mod.rs index 4191e3e96b0..ab966d4a360 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] -- cgit 1.4.1-3-g733a5 From 5ff7b632a95bac6955611d85040859128902c580 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 29 Mar 2022 12:59:07 -0700 Subject: 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(-) (limited to 'src') diff --git a/src/macros.rs b/src/macros.rs index fdbe3374615..664f152e8be 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 5b981385d2b..f51ba6e98c9 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 570d21c17df..d1741360cfd 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! {#} } -- cgit 1.4.1-3-g733a5