diff --git a/src/tools/rustfmt/.github/workflows/linux.yml b/src/tools/rustfmt/.github/workflows/linux.yml index db49794164212..45f63b83c0562 100644 --- a/src/tools/rustfmt/.github/workflows/linux.yml +++ b/src/tools/rustfmt/.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/tools/rustfmt/CHANGELOG.md b/src/tools/rustfmt/CHANGELOG.md index b59438dc4fe78..5b23360858356 100644 --- a/src/tools/rustfmt/CHANGELOG.md +++ b/src/tools/rustfmt/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 @@ -57,6 +61,7 @@ Note this hit the rustup distributions prior to the v1.4.38 release as part of a - New `One` variant added to `imports_granularity` configuration option which can be used to reformat all imports into a single use statement [#4669](https://github.com/rust-lang/rustfmt/issues/4669) - rustfmt will now skip files that are annotated with `@generated` at the top of the file [#3958](https://github.com/rust-lang/rustfmt/issues/3958) + if `format_generated_files` option is set to `false` (by default `@generated` files are formatted) - New configuration option `hex_literal_case` that allows user to control the casing utilized for hex literals [PR #4903](https://github.com/rust-lang/rustfmt/pull/4903) See the section on the configuration site for more information diff --git a/src/tools/rustfmt/Cargo.lock b/src/tools/rustfmt/Cargo.lock index 2ef83ddd1ae6c..b932e15ef7461 100644 --- a/src/tools/rustfmt/Cargo.lock +++ b/src/tools/rustfmt/Cargo.lock @@ -275,9 +275,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.9.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" dependencies = [ "either", ] diff --git a/src/tools/rustfmt/Configurations.md b/src/tools/rustfmt/Configurations.md index 4476f2a449b1f..a47439b9ba96c 100644 --- a/src/tools/rustfmt/Configurations.md +++ b/src/tools/rustfmt/Configurations.md @@ -930,6 +930,8 @@ fn add_one(x: i32) -> i32 { Format generated files. A file is considered generated if any of the first five lines contain a `@generated` comment marker. +By default, generated files are reformatted, i. e. `@generated` marker is ignored. +This option is currently ignored for stdin (`@generated` in stdin is ignored.) - **Default value**: `true` - **Possible values**: `true`, `false` @@ -2198,13 +2200,47 @@ 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 - **Default value**: `false` - **Possible values**: `true`, `false` -- **Stable**: No (tracking issue: [#3389](https://github.com/rust-lang/rustfmt/issues/3386)) +- **Stable**: No (tracking issue: [#3389](https://github.com/rust-lang/rustfmt/issues/3389)) ## `single_line_if_else_max_width` diff --git a/src/tools/rustfmt/config_proc_macro/src/utils.rs b/src/tools/rustfmt/config_proc_macro/src/utils.rs index 5b68d2748490f..f5cba87b07b67 100644 --- a/src/tools/rustfmt/config_proc_macro/src/utils.rs +++ b/src/tools/rustfmt/config_proc_macro/src/utils.rs @@ -22,10 +22,10 @@ pub fn is_unit(v: &syn::Variant) -> bool { #[cfg(feature = "debug-with-rustfmt")] /// Pretty-print the output of proc macro using rustfmt. pub fn debug_with_rustfmt(input: &TokenStream) { - use std::io::Write; - use std::process::{Command, Stdio}; use std::env; use std::ffi::OsStr; + use std::io::Write; + use std::process::{Command, Stdio}; let rustfmt_var = env::var_os("RUSTFMT"); let rustfmt = match &rustfmt_var { diff --git a/src/tools/rustfmt/rust-toolchain b/src/tools/rustfmt/rust-toolchain index d4cdcec2018aa..94b57d506c20b 100644 --- a/src/tools/rustfmt/rust-toolchain +++ b/src/tools/rustfmt/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-12-29" +channel = "nightly-2022-03-27" components = ["rustc-dev"] diff --git a/src/tools/rustfmt/src/attr.rs b/src/tools/rustfmt/src/attr.rs index 3887a8051f209..befe12ae2c4cd 100644 --- a/src/tools/rustfmt/src/attr.rs +++ b/src/tools/rustfmt/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/src/tools/rustfmt/src/bin/main.rs b/src/tools/rustfmt/src/bin/main.rs index 4d845547cdfed..ad10b9ede608f 100644 --- a/src/tools/rustfmt/src/bin/main.rs +++ b/src/tools/rustfmt/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 } }; @@ -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 @@ -393,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/src/tools/rustfmt/src/cargo-fmt/main.rs b/src/tools/rustfmt/src/cargo-fmt/main.rs index 759b21218c353..8cb7b4585ecb2 100644 --- a/src/tools/rustfmt/src/cargo-fmt/main.rs +++ b/src/tools/rustfmt/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/tools/rustfmt/src/comment.rs b/src/tools/rustfmt/src/comment.rs index 0f850b9b2f2fb..f9d8a0fa70c00 100644 --- a/src/tools/rustfmt/src/comment.rs +++ b/src/tools/rustfmt/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, @@ -651,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; @@ -757,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'); @@ -850,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/src/tools/rustfmt/src/config/file_lines.rs b/src/tools/rustfmt/src/config/file_lines.rs index 7b498dc46b320..e4e51a3f3b409 100644 --- a/src/tools/rustfmt/src/config/file_lines.rs +++ b/src/tools/rustfmt/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/tools/rustfmt/src/config/mod.rs b/src/tools/rustfmt/src/config/mod.rs index cd90e0904b6cd..18e1854612bf7 100644 --- a/src/tools/rustfmt/src/config/mod.rs +++ b/src/tools/rustfmt/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, @@ -364,7 +366,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)); } } _ => {} @@ -589,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/tools/rustfmt/src/emitter/checkstyle.rs b/src/tools/rustfmt/src/emitter/checkstyle.rs index 76f2527db3dad..545b259979d91 100644 --- a/src/tools/rustfmt/src/emitter/checkstyle.rs +++ b/src/tools/rustfmt/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/tools/rustfmt/src/emitter/diff.rs b/src/tools/rustfmt/src/emitter/diff.rs index 7264ad8bbf365..5e1f134465605 100644 --- a/src/tools/rustfmt/src/emitter/diff.rs +++ b/src/tools/rustfmt/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/tools/rustfmt/src/emitter/json.rs b/src/tools/rustfmt/src/emitter/json.rs index 269dd2d4daf57..c7f68d4675a67 100644 --- a/src/tools/rustfmt/src/emitter/json.rs +++ b/src/tools/rustfmt/src/emitter/json.rs @@ -3,14 +3,13 @@ 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 { - 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, @@ -20,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, @@ -47,71 +40,67 @@ 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(); 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: &Path, - 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 = 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) - } - 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.push_str(&msg); + expected.push('\n'); + } + DiffLine::Resulting(msg) => { + original_end_line = original_begin_line + original_line_counter; + original_line_counter += 1; + original.push_str(&msg); + original.push('\n'); + } + 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, + expected, + }); + } + self.mismatched_files.push(MismatchedFile { + name: format!("{}", filename), + mismatches, }); + Ok(()) } - let json = to_json_string(&MismatchedFile { - name: String::from(filename.to_str().unwrap()), - mismatches, - })?; - let prefix = if num_emitted_files > 0 { "," } else { "" }; - write!(writer, "{}{}", prefix, &json)?; - Ok(()) } #[cfg(test)] @@ -122,6 +111,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), @@ -130,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 { @@ -146,14 +138,19 @@ 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); - 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), @@ -163,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", ), }], }; @@ -186,10 +183,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); - 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] @@ -209,7 +208,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] @@ -255,7 +254,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 { @@ -263,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, @@ -272,17 +271,17 @@ 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", ), }, ], - }) + }]) .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] @@ -317,33 +316,31 @@ 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, 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"), }], - }) - .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, 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"), }], - }) - .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/src/tools/rustfmt/src/format_report_formatter.rs b/src/tools/rustfmt/src/format_report_formatter.rs index c820259256c47..90406cdb95e2b 100644 --- a/src/tools/rustfmt/src/format_report_formatter.rs +++ b/src/tools/rustfmt/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/tools/rustfmt/src/ignore_path.rs b/src/tools/rustfmt/src/ignore_path.rs index 7738eee0a7604..d955949496a67 100644 --- a/src/tools/rustfmt/src/ignore_path.rs +++ b/src/tools/rustfmt/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/tools/rustfmt/src/imports.rs b/src/tools/rustfmt/src/imports.rs index 40e0d06f99df8..0231980948686 100644 --- a/src/tools/rustfmt/src/imports.rs +++ b/src/tools/rustfmt/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() } @@ -238,7 +232,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 +617,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; @@ -633,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( @@ -1309,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/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index 8498cb6addaa1..92f423bbb6275 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/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( @@ -1377,17 +1383,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)); } } @@ -2046,9 +2056,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/src/tools/rustfmt/src/lists.rs b/src/tools/rustfmt/src/lists.rs index 29d75585eb725..e87850507824f 100644 --- a/src/tools/rustfmt/src/lists.rs +++ b/src/tools/rustfmt/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/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index f29552caf8d87..664f152e8be1d 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/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( @@ -152,7 +157,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 { @@ -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/src/tools/rustfmt/src/modules.rs b/src/tools/rustfmt/src/modules.rs index 64d96a5c6a6e4..a65dc66f7972e 100644 --- a/src/tools/rustfmt/src/modules.rs +++ b/src/tools/rustfmt/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(), + }, + }), + }, } } @@ -458,6 +484,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 +495,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/tools/rustfmt/src/overflow.rs b/src/tools/rustfmt/src/overflow.rs index 3475f5c378cd2..80aed998d7377 100644 --- a/src/tools/rustfmt/src/overflow.rs +++ b/src/tools/rustfmt/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/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 412f4434b9ead..7571e6d078a7b 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/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,33 @@ 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 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) + } + }, + ) } pub(crate) fn is_file_parsed(&self, path: &Path) -> bool { @@ -197,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) } diff --git a/src/tools/rustfmt/src/spanned.rs b/src/tools/rustfmt/src/spanned.rs index 8e6c75a3744ac..2136cfeae1af1 100644 --- a/src/tools/rustfmt/src/spanned.rs +++ b/src/tools/rustfmt/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/tools/rustfmt/src/string.rs b/src/tools/rustfmt/src/string.rs index 64ae15672df8f..b65aa5b33b241 100644 --- a/src/tools/rustfmt/src/string.rs +++ b/src/tools/rustfmt/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/src/tools/rustfmt/src/test/configuration_snippet.rs b/src/tools/rustfmt/src/test/configuration_snippet.rs index 92949ab576a6b..c8fda7c8556db 100644 --- a/src/tools/rustfmt/src/test/configuration_snippet.rs +++ b/src/tools/rustfmt/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, + ); + } + } +} diff --git a/src/tools/rustfmt/src/test/mod.rs b/src/tools/rustfmt/src/test/mod.rs index c50d18644b091..ab966d4a36075 100644 --- a/src/tools/rustfmt/src/test/mod.rs +++ b/src/tools/rustfmt/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] @@ -420,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] @@ -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(); @@ -502,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] @@ -914,3 +987,52 @@ 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()); +} + +#[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(), "\n"); +} diff --git a/src/tools/rustfmt/src/test/mod_resolver.rs b/src/tools/rustfmt/src/test/mod_resolver.rs index ec9ed0f0b8d62..aacb2acc68498 100644 --- a/src/tools/rustfmt/src/test/mod_resolver.rs +++ b/src/tools/rustfmt/src/test/mod_resolver.rs @@ -50,3 +50,33 @@ 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", + ], + ) +} + +#[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/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index a49d473a13f3f..64a201e45ddd4 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/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/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 2428d8cb0fd89..35512e78fa6e2 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/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/src/tools/rustfmt/src/vertical.rs b/src/tools/rustfmt/src/vertical.rs index c4208848c6c2a..a06bc995aa55e 100644 --- a/src/tools/rustfmt/src/vertical.rs +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/cargo-fmt/main.rs b/src/tools/rustfmt/tests/cargo-fmt/main.rs index 5493b09e4aa63..348876cd264fa 100644 --- a/src/tools/rustfmt/tests/cargo-fmt/main.rs +++ b/src/tools/rustfmt/tests/cargo-fmt/main.rs @@ -1,6 +1,7 @@ // Integration tests for cargo-fmt. use std::env; +use std::path::Path; use std::process::Command; /// Run the cargo-fmt executable and return its output. @@ -71,3 +72,27 @@ fn rustfmt_help() { assert_that!(&["--", "-h"], contains("Format Rust code")); assert_that!(&["--", "--help=config"], contains("Configuration Options:")); } + +#[ignore] +#[test] +fn cargo_fmt_out_of_line_test_modules() { + // See also https://github.com/rust-lang/rustfmt/issues/5119 + 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/src/tools/rustfmt/tests/mod-resolver/issue-5167/src/a.rs b/src/tools/rustfmt/tests/mod-resolver/issue-5167/src/a.rs new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/tools/rustfmt/tests/mod-resolver/issue-5167/src/a/mod.rs b/src/tools/rustfmt/tests/mod-resolver/issue-5167/src/a/mod.rs new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/tools/rustfmt/tests/mod-resolver/issue-5167/src/lib.rs b/src/tools/rustfmt/tests/mod-resolver/issue-5167/src/lib.rs new file mode 100644 index 0000000000000..f21af614da057 --- /dev/null +++ b/src/tools/rustfmt/tests/mod-resolver/issue-5167/src/lib.rs @@ -0,0 +1 @@ +mod a; diff --git a/src/tools/rustfmt/tests/mod-resolver/issue-5198/a.rs b/src/tools/rustfmt/tests/mod-resolver/issue-5198/a.rs new file mode 100644 index 0000000000000..cd686f5611690 --- /dev/null +++ b/src/tools/rustfmt/tests/mod-resolver/issue-5198/a.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib.rs b/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib.rs new file mode 100644 index 0000000000000..696832913c879 --- /dev/null +++ b/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib.rs @@ -0,0 +1,3 @@ +mod a; +mod b; +mod c; diff --git a/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/b.rs b/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/b.rs new file mode 100644 index 0000000000000..cd686f5611690 --- /dev/null +++ b/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/b.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/d.rs b/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/d.rs new file mode 100644 index 0000000000000..d1604aa23a3cb --- /dev/null +++ b/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/d.rs @@ -0,0 +1,3 @@ +mod e; +mod f; +mod g; diff --git a/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt b/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt new file mode 100644 index 0000000000000..92c9e3021431f --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/d/f.rs b/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/d/f.rs new file mode 100644 index 0000000000000..cd686f5611690 --- /dev/null +++ b/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/d/f.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs b/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs new file mode 100644 index 0000000000000..cd686f5611690 --- /dev/null +++ b/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/e.rs b/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/e.rs new file mode 100644 index 0000000000000..cd686f5611690 --- /dev/null +++ b/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/e.rs @@ -0,0 +1 @@ +fn main( ) { println!("Hello World!") } diff --git a/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/mod.rs b/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/mod.rs new file mode 100644 index 0000000000000..81904619650f9 --- /dev/null +++ b/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/c/mod.rs @@ -0,0 +1,3 @@ +mod d; + +fn main( ) { println!("Hello World!") } diff --git a/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/explanation.txt b/src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/explanation.txt new file mode 100644 index 0000000000000..d436a8076cd71 --- /dev/null +++ b/src/tools/rustfmt/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. diff --git a/src/tools/rustfmt/tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs b/src/tools/rustfmt/tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs new file mode 100644 index 0000000000000..2a63c961be8fc --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/mod-resolver/module-not-found/relative_module/a.rs b/src/tools/rustfmt/tests/mod-resolver/module-not-found/relative_module/a.rs new file mode 100644 index 0000000000000..4a1eac8965ded --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/mod-resolver/module-not-found/relative_module/lib.rs b/src/tools/rustfmt/tests/mod-resolver/module-not-found/relative_module/lib.rs new file mode 100644 index 0000000000000..f21af614da057 --- /dev/null +++ b/src/tools/rustfmt/tests/mod-resolver/module-not-found/relative_module/lib.rs @@ -0,0 +1 @@ +mod a; diff --git a/src/tools/rustfmt/tests/mod-resolver/module-not-found/sibling_module/lib.rs b/src/tools/rustfmt/tests/mod-resolver/module-not-found/sibling_module/lib.rs new file mode 100644 index 0000000000000..d9d9e1e3c9087 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml b/src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml new file mode 100644 index 0000000000000..0993f12795991 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs b/src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs new file mode 100644 index 0000000000000..3f7ddba8a288c --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs b/src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs new file mode 100644 index 0000000000000..da4e86169ad92 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs b/src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs new file mode 100644 index 0000000000000..b760ba23cd277 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs b/src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs new file mode 100644 index 0000000000000..4fd8286eac400 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs b/src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs new file mode 100644 index 0000000000000..e029785bc2457 --- /dev/null +++ b/src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs @@ -0,0 +1 @@ +mod sub4; diff --git a/src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs b/src/tools/rustfmt/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/tools/rustfmt/tests/rustfmt/main.rs b/src/tools/rustfmt/tests/rustfmt/main.rs index 8effb1c6fcab6..450051d2fec61 100644 --- a/src/tools/rustfmt/tests/rustfmt/main.rs +++ b/src/tools/rustfmt/tests/rustfmt/main.rs @@ -106,3 +106,54 @@ fn inline_config() { && contains("format_strings = true") ); } + +#[test] +fn rustfmt_usage_text() { + let args = ["--help"]; + 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")); +} diff --git a/src/tools/rustfmt/tests/source/5131_crate.rs b/src/tools/rustfmt/tests/source/5131_crate.rs new file mode 100644 index 0000000000000..96a31659022aa --- /dev/null +++ b/src/tools/rustfmt/tests/source/5131_crate.rs @@ -0,0 +1,14 @@ +// rustfmt-imports_granularity: Crate + +use foo::a; +use foo::a; +use foo::b; +use foo::b as b2; +use foo::b::f; +use foo::b::g; +use foo::b::g as g2; +use foo::c; +use foo::d::e; +use qux::h; +use qux::h as h2; +use qux::i; diff --git a/src/tools/rustfmt/tests/source/5131_module.rs b/src/tools/rustfmt/tests/source/5131_module.rs new file mode 100644 index 0000000000000..3e9139177c56e --- /dev/null +++ b/src/tools/rustfmt/tests/source/5131_module.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/src/tools/rustfmt/tests/source/5131_one.rs b/src/tools/rustfmt/tests/source/5131_one.rs new file mode 100644 index 0000000000000..61ddf13410d4b --- /dev/null +++ b/src/tools/rustfmt/tests/source/5131_one.rs @@ -0,0 +1,15 @@ +// rustfmt-imports_granularity: One + +pub use foo::x; +pub use foo::x as x2; +pub use foo::y; +use bar::a; +use bar::b; +use bar::b::f; +use bar::b::f as f2; +use bar::b::g; +use bar::c; +use bar::d::e; +use bar::d::e as e2; +use qux::h; +use qux::i; diff --git a/src/tools/rustfmt/tests/source/configs/short_array_element_width_threshold/10.rs b/src/tools/rustfmt/tests/source/configs/short_array_element_width_threshold/10.rs new file mode 100644 index 0000000000000..7d0d70919a607 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/configs/short_array_element_width_threshold/20.rs b/src/tools/rustfmt/tests/source/configs/short_array_element_width_threshold/20.rs new file mode 100644 index 0000000000000..8a93a51d6a281 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/configs/short_array_element_width_threshold/greater_than_max_width.rs b/src/tools/rustfmt/tests/source/configs/short_array_element_width_threshold/greater_than_max_width.rs new file mode 100644 index 0000000000000..710b6fe7c4ba0 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/extern.rs b/src/tools/rustfmt/tests/source/extern.rs index d0a033b12432a..f51ba6e98c9f5 100644 --- a/src/tools/rustfmt/tests/source/extern.rs +++ b/src/tools/rustfmt/tests/source/extern.rs @@ -77,3 +77,16 @@ libc::c_long; extern { } + +macro_rules! x { + ($tt:tt) => {}; +} + +extern "macros" { + x!(ident); + x!(#); + x![ident]; + x![#]; + x! {ident} + x! {#} +} diff --git a/src/tools/rustfmt/tests/source/imports_granularity_module.rs b/src/tools/rustfmt/tests/source/imports_granularity_module.rs index 5a4fad5872bdd..2d7bb299aaace 100644 --- a/src/tools/rustfmt/tests/source/imports_granularity_module.rs +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/issue-4036/one.rs b/src/tools/rustfmt/tests/source/issue-4036/one.rs new file mode 100644 index 0000000000000..9f9675f51631a --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/issue-4036/three.rs b/src/tools/rustfmt/tests/source/issue-4036/three.rs new file mode 100644 index 0000000000000..e1865dd0868b6 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/issue-4036/two.rs b/src/tools/rustfmt/tests/source/issue-4036/two.rs new file mode 100644 index 0000000000000..fa54d2e3e09cb --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/issue-4791/buggy.rs b/src/tools/rustfmt/tests/source/issue-4791/buggy.rs new file mode 100644 index 0000000000000..4760022eeaf0d --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-4791/buggy.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/src/tools/rustfmt/tests/source/issue-4791/trailing_comma.rs b/src/tools/rustfmt/tests/source/issue-4791/trailing_comma.rs new file mode 100644 index 0000000000000..c56c70faeae40 --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-4791/trailing_comma.rs @@ -0,0 +1,14 @@ +// rustfmt-struct_field_align_threshold: 30 +// rustfmt-trailing_comma: Always + +struct Foo { + group_a: u8, + + group_b: u8 +} + +struct Bar { + group_a: u8, + + group_b: u8, +} diff --git a/src/tools/rustfmt/tests/source/issue-5023.rs b/src/tools/rustfmt/tests/source/issue-5023.rs new file mode 100644 index 0000000000000..ae1c723eff76a --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs b/src/tools/rustfmt/tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs new file mode 100644 index 0000000000000..5d171f32a1aea --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs b/src/tools/rustfmt/tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs new file mode 100644 index 0000000000000..b8a824b34b796 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/issue-5042/single-line_comment_with_trailing_comma.rs b/src/tools/rustfmt/tests/source/issue-5042/single-line_comment_with_trailing_comma.rs new file mode 100644 index 0000000000000..bd765b7b41f49 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/issue-5042/single-line_comment_without_trailing_comma.rs b/src/tools/rustfmt/tests/source/issue-5042/single-line_comment_without_trailing_comma.rs new file mode 100644 index 0000000000000..2ed8de875add8 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/issue-5157/indented_itemized_markdown_blockquote.rs b/src/tools/rustfmt/tests/source/issue-5157/indented_itemized_markdown_blockquote.rs new file mode 100644 index 0000000000000..5c1d79a743098 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/issue-5157/nested_itemized_markdown_blockquote.rs b/src/tools/rustfmt/tests/source/issue-5157/nested_itemized_markdown_blockquote.rs new file mode 100644 index 0000000000000..cf200d04e08ef --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/issue-5157/support_itemized_markdown_blockquote.rs b/src/tools/rustfmt/tests/source/issue-5157/support_itemized_markdown_blockquote.rs new file mode 100644 index 0000000000000..eb436402e4e00 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/issue-5238/markdown_header_wrap_comments_false.rs b/src/tools/rustfmt/tests/source/issue-5238/markdown_header_wrap_comments_false.rs new file mode 100644 index 0000000000000..229c6e5753d2e --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/issue-5238/markdown_header_wrap_comments_true.rs b/src/tools/rustfmt/tests/source/issue-5238/markdown_header_wrap_comments_true.rs new file mode 100644 index 0000000000000..c547ff35c691b --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/issue-5270/merge_derives_true.rs b/src/tools/rustfmt/tests/source/issue-5270/merge_derives_true.rs new file mode 100644 index 0000000000000..b31bbf095e730 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/source/issue_4854.rs b/src/tools/rustfmt/tests/source/issue_4854.rs new file mode 100644 index 0000000000000..35d6e21affeeb --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/5131_crate.rs b/src/tools/rustfmt/tests/target/5131_crate.rs new file mode 100644 index 0000000000000..557d667035546 --- /dev/null +++ b/src/tools/rustfmt/tests/target/5131_crate.rs @@ -0,0 +1,9 @@ +// rustfmt-imports_granularity: Crate + +use foo::{ + a, b, b as b2, + b::{f, g, g as g2}, + c, + d::e, +}; +use qux::{h, h as h2, i}; diff --git a/src/tools/rustfmt/tests/target/5131_module.rs b/src/tools/rustfmt/tests/target/5131_module.rs new file mode 100644 index 0000000000000..763024d6fa495 --- /dev/null +++ b/src/tools/rustfmt/tests/target/5131_module.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); + } + } +} diff --git a/src/tools/rustfmt/tests/target/5131_one.rs b/src/tools/rustfmt/tests/target/5131_one.rs new file mode 100644 index 0000000000000..a086dae5a4221 --- /dev/null +++ b/src/tools/rustfmt/tests/target/5131_one.rs @@ -0,0 +1,12 @@ +// rustfmt-imports_granularity: One + +pub use foo::{x, x as x2, y}; +use { + bar::{ + a, + b::{self, f, g}, + c, + d::{e, e as e2}, + }, + qux::{h, i}, +}; diff --git a/src/tools/rustfmt/tests/target/comments-in-lists/format-doc-comments.rs b/src/tools/rustfmt/tests/target/comments-in-lists/format-doc-comments.rs index be31bf0a33198..be4b7a8c42e3e 100644 --- a/src/tools/rustfmt/tests/target/comments-in-lists/format-doc-comments.rs +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/comments-in-lists/wrap-comments-false.rs b/src/tools/rustfmt/tests/target/comments-in-lists/wrap-comments-false.rs index 80aea59d1b520..db4da6223721c 100644 --- a/src/tools/rustfmt/tests/target/comments-in-lists/wrap-comments-false.rs +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/comments-in-lists/wrap-comments-not-normalized.rs b/src/tools/rustfmt/tests/target/comments-in-lists/wrap-comments-not-normalized.rs index 52315f470e4b9..9b9147eb1247a 100644 --- a/src/tools/rustfmt/tests/target/comments-in-lists/wrap-comments-not-normalized.rs +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/comments-in-lists/wrap-comments-true.rs b/src/tools/rustfmt/tests/target/comments-in-lists/wrap-comments-true.rs index e0bfcf0b5007d..c1531d22a4a70 100644 --- a/src/tools/rustfmt/tests/target/comments-in-lists/wrap-comments-true.rs +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/configs/short_array_element_width_threshold/10.rs b/src/tools/rustfmt/tests/target/configs/short_array_element_width_threshold/10.rs new file mode 100644 index 0000000000000..78c4adba1c1f1 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/configs/short_array_element_width_threshold/20.rs b/src/tools/rustfmt/tests/target/configs/short_array_element_width_threshold/20.rs new file mode 100644 index 0000000000000..6084690652f06 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/configs/short_array_element_width_threshold/greater_than_max_width.rs b/src/tools/rustfmt/tests/target/configs/short_array_element_width_threshold/greater_than_max_width.rs new file mode 100644 index 0000000000000..710b6fe7c4ba0 --- /dev/null +++ b/src/tools/rustfmt/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, + ]; +} diff --git a/src/tools/rustfmt/tests/target/doc-of-generic-item.rs b/src/tools/rustfmt/tests/target/doc-of-generic-item.rs new file mode 100644 index 0000000000000..2efc5e09a3d34 --- /dev/null +++ b/src/tools/rustfmt/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, +>; diff --git a/src/tools/rustfmt/tests/target/extern.rs b/src/tools/rustfmt/tests/target/extern.rs index 44ed6d4b4756d..d1741360cfd64 100644 --- a/src/tools/rustfmt/tests/target/extern.rs +++ b/src/tools/rustfmt/tests/target/extern.rs @@ -82,3 +82,16 @@ extern "C" { } extern "C" {} + +macro_rules! x { + ($tt:tt) => {}; +} + +extern "macros" { + x!(ident); + x!(#); + x![ident]; + x![#]; + x! {ident} + x! {#} +} diff --git a/src/tools/rustfmt/tests/target/imports_granularity_module.rs b/src/tools/rustfmt/tests/target/imports_granularity_module.rs index 9c1387c466afa..e4e1a299e5866 100644 --- a/src/tools/rustfmt/tests/target/imports_granularity_module.rs +++ b/src/tools/rustfmt/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)] diff --git a/src/tools/rustfmt/tests/target/issue-4036/one.rs b/src/tools/rustfmt/tests/target/issue-4036/one.rs new file mode 100644 index 0000000000000..54e490b7fbeae --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/issue-4036/three.rs b/src/tools/rustfmt/tests/target/issue-4036/three.rs new file mode 100644 index 0000000000000..394dc8633f534 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/issue-4036/two.rs b/src/tools/rustfmt/tests/target/issue-4036/two.rs new file mode 100644 index 0000000000000..01cafa76b6842 --- /dev/null +++ b/src/tools/rustfmt/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.", + ) + } + }; +} diff --git a/src/tools/rustfmt/tests/target/issue-4791/buggy.rs b/src/tools/rustfmt/tests/target/issue-4791/buggy.rs new file mode 100644 index 0000000000000..fff58be99a509 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-4791/buggy.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/src/tools/rustfmt/tests/target/issue-4791/issue_4928.rs b/src/tools/rustfmt/tests/target/issue-4791/issue_4928.rs new file mode 100644 index 0000000000000..588656b535fa1 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-4791/issue_4928.rs @@ -0,0 +1,70 @@ +// rustfmt-brace_style: SameLineWhere +// rustfmt-comment_width: 100 +// rustfmt-edition: 2018 +// rustfmt-fn_args_layout: Compressed +// rustfmt-hard_tabs: false +// rustfmt-match_block_trailing_comma: true +// rustfmt-max_width: 100 +// rustfmt-merge_derives: false +// rustfmt-newline_style: Unix +// rustfmt-normalize_doc_attributes: true +// rustfmt-overflow_delimited_expr: true +// rustfmt-reorder_imports: false +// rustfmt-reorder_modules: true +// rustfmt-struct_field_align_threshold: 20 +// rustfmt-tab_spaces: 4 +// rustfmt-trailing_comma: Never +// rustfmt-use_small_heuristics: Max +// rustfmt-use_try_shorthand: true +// rustfmt-wrap_comments: true + +/// Lorem ipsum dolor sit amet. +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] +pub struct BufferAttr { + /* NOTE: Blah blah blah blah blah. */ + /// Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + /// ut labore et dolore magna aliqua. Morbi quis commodo odio aenean sed adipiscing. Nunc + /// congue nisi vitae suscipit tellus mauris a. Consectetur adipiscing elit pellentesque + /// habitant morbi tristique senectus. + pub foo: u32, + + /// Elit eget gravida cum sociis natoque penatibus et magnis dis. Consequat semper viverra nam + /// libero. Accumsan in nisl nisi scelerisque eu. Pellentesque id nibh tortor id aliquet. Sed + /// velit dignissim sodales ut. Facilisis sed odio morbi quis commodo odio aenean sed. Et + /// ultrices neque ornare aenean euismod elementum. Condimentum lacinia quis vel eros donec ac + /// odio tempor. + /// + /// Lacinia at quis risus sed vulputate odio ut enim. Etiam erat velit scelerisque in dictum. + /// Nibh tellus molestie nunc non blandit massa enim nec. Nascetur ridiculus mus mauris vitae. + pub bar: u32, + + /// Mi proin sed libero enim sed faucibus turpis. Amet consectetur adipiscing elit duis + /// tristique sollicitudin nibh sit amet. Congue quisque egestas diam in arcu cursus euismod + /// quis viverra. Cum sociis natoque penatibus et magnis dis parturient montes. Enim sit amet + /// venenatis urna cursus eget nunc scelerisque viverra. Cras semper auctor neque vitae tempus + /// quam pellentesque. Tortor posuere ac ut consequat semper viverra nam libero justo. Vitae + /// auctor eu augue ut lectus arcu bibendum at. Faucibus vitae aliquet nec ullamcorper sit amet + /// risus nullam. Maecenas accumsan lacus vel facilisis volutpat. Arcu non odio euismod + /// lacinia. + /// + /// [`FooBar::beep()`]: crate::foobar::FooBar::beep + /// [`FooBar::boop()`]: crate::foobar::FooBar::boop + /// [`foobar::BazBaq::BEEP_BOOP`]: crate::foobar::BazBaq::BEEP_BOOP + pub baz: u32, + + /// Eu consequat ac felis donec et odio pellentesque diam. Ut eu sem integer vitae justo eget. + /// Consequat ac felis donec et odio pellentesque diam volutpat. + pub baq: u32, + + /// Amet consectetur adipiscing elit pellentesque habitant. Ut morbi tincidunt augue interdum + /// velit euismod in pellentesque. Imperdiet sed euismod nisi porta lorem. Nec tincidunt + /// praesent semper feugiat. Facilisis leo vel fringilla est. Egestas diam in arcu cursus + /// euismod quis viverra. Sagittis eu volutpat odio facilisis mauris sit amet. Posuere morbi + /// leo urna molestie at. + /// + /// Pretium aenean pharetra magna ac. Nisl condimentum id venenatis a condimentum vitae. Semper + /// quis lectus nulla at volutpat diam ut venenatis tellus. Egestas tellus rutrum tellus + /// pellentesque eu tincidunt tortor aliquam. + pub foobar: u32 +} diff --git a/src/tools/rustfmt/tests/target/issue-4791/no_trailing_comma.rs b/src/tools/rustfmt/tests/target/issue-4791/no_trailing_comma.rs new file mode 100644 index 0000000000000..4a37163969ae9 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-4791/no_trailing_comma.rs @@ -0,0 +1,8 @@ +// rustfmt-struct_field_align_threshold: 0 +// rustfmt-trailing_comma: Never + +pub struct Baz { + group_a: u8, + + group_b: u8 +} diff --git a/src/tools/rustfmt/tests/target/issue-4791/trailing_comma.rs b/src/tools/rustfmt/tests/target/issue-4791/trailing_comma.rs new file mode 100644 index 0000000000000..29a224b3f6d96 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-4791/trailing_comma.rs @@ -0,0 +1,14 @@ +// rustfmt-struct_field_align_threshold: 30 +// rustfmt-trailing_comma: Always + +struct Foo { + group_a: u8, + + group_b: u8, +} + +struct Bar { + group_a: u8, + + group_b: u8, +} diff --git a/src/tools/rustfmt/tests/target/issue-5023.rs b/src/tools/rustfmt/tests/target/issue-5023.rs new file mode 100644 index 0000000000000..4e84c7d98427a --- /dev/null +++ b/src/tools/rustfmt/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(); + } +} diff --git a/src/tools/rustfmt/tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs b/src/tools/rustfmt/tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs new file mode 100644 index 0000000000000..1ae1212b488d9 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs b/src/tools/rustfmt/tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs new file mode 100644 index 0000000000000..30d174664c9cf --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/issue-5042/single-line_comment_with_trailing_comma.rs b/src/tools/rustfmt/tests/target/issue-5042/single-line_comment_with_trailing_comma.rs new file mode 100644 index 0000000000000..87b651dd285ef --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/issue-5042/single-line_comment_without_trailing_comma.rs b/src/tools/rustfmt/tests/target/issue-5042/single-line_comment_without_trailing_comma.rs new file mode 100644 index 0000000000000..116df86a4b554 --- /dev/null +++ b/src/tools/rustfmt/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 /* ... */); +} diff --git a/src/tools/rustfmt/tests/target/issue-5125/attributes_in_formal_fuction_parameter.rs b/src/tools/rustfmt/tests/target/issue-5125/attributes_in_formal_fuction_parameter.rs new file mode 100644 index 0000000000000..5d167932828f3 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5125/attributes_in_formal_fuction_parameter.rs @@ -0,0 +1,6 @@ +fn foo( + #[unused] a: >::ForeignType, +) { +} diff --git a/src/tools/rustfmt/tests/target/issue-5125/long_parameter_in_different_positions.rs b/src/tools/rustfmt/tests/target/issue-5125/long_parameter_in_different_positions.rs new file mode 100644 index 0000000000000..cab20381ce8f7 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/issue-5125/minimum_example.rs b/src/tools/rustfmt/tests/target/issue-5125/minimum_example.rs new file mode 100644 index 0000000000000..8003e66968c78 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5125/minimum_example.rs @@ -0,0 +1,6 @@ +fn foo( + a: >::ForeignType, +) { +} diff --git a/src/tools/rustfmt/tests/target/issue-5125/with_leading_and_inline_comments.rs b/src/tools/rustfmt/tests/target/issue-5125/with_leading_and_inline_comments.rs new file mode 100644 index 0000000000000..2340b2f3472e9 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-5125/with_leading_and_inline_comments.rs @@ -0,0 +1,7 @@ +fn foo( + // Pre Comment + a: >::ForeignType, // Inline comment +) { +} diff --git a/src/tools/rustfmt/tests/target/issue-5157/indented_itemized_markdown_blockquote.rs b/src/tools/rustfmt/tests/target/issue-5157/indented_itemized_markdown_blockquote.rs new file mode 100644 index 0000000000000..e47677f203903 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/issue-5157/nested_itemized_markdown_blockquote.rs b/src/tools/rustfmt/tests/target/issue-5157/nested_itemized_markdown_blockquote.rs new file mode 100644 index 0000000000000..079510442b799 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/issue-5157/support_itemized_markdown_blockquote.rs b/src/tools/rustfmt/tests/target/issue-5157/support_itemized_markdown_blockquote.rs new file mode 100644 index 0000000000000..029ee37d22a8b --- /dev/null +++ b/src/tools/rustfmt/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() {} diff --git a/src/tools/rustfmt/tests/target/issue-5238/markdown_header_wrap_comments_false.rs b/src/tools/rustfmt/tests/target/issue-5238/markdown_header_wrap_comments_false.rs new file mode 100644 index 0000000000000..229c6e5753d2e --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/issue-5238/markdown_header_wrap_comments_true.rs b/src/tools/rustfmt/tests/target/issue-5238/markdown_header_wrap_comments_true.rs new file mode 100644 index 0000000000000..87dae58eccd73 --- /dev/null +++ b/src/tools/rustfmt/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` +} diff --git a/src/tools/rustfmt/tests/target/issue-5270/merge_derives_false.rs b/src/tools/rustfmt/tests/target/issue-5270/merge_derives_false.rs new file mode 100644 index 0000000000000..3b6f7e66993c0 --- /dev/null +++ b/src/tools/rustfmt/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/src/tools/rustfmt/tests/target/issue-5270/merge_derives_true.rs b/src/tools/rustfmt/tests/target/issue-5270/merge_derives_true.rs new file mode 100644 index 0000000000000..5f488b4542d0b --- /dev/null +++ b/src/tools/rustfmt/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, + } +} diff --git a/src/tools/rustfmt/tests/target/issue_4854.rs b/src/tools/rustfmt/tests/target/issue_4854.rs new file mode 100644 index 0000000000000..a81c5a5171fb5 --- /dev/null +++ b/src/tools/rustfmt/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 */} +} diff --git a/src/tools/rustfmt/tests/target/issue_5273.rs b/src/tools/rustfmt/tests/target/issue_5273.rs new file mode 100644 index 0000000000000..3bb9048a5fd3d --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue_5273.rs @@ -0,0 +1,3 @@ +struct Example { + // +} diff --git a/src/tools/rustfmt/tests/writemode/source/stdin.rs b/src/tools/rustfmt/tests/writemode/source/stdin.rs new file mode 100644 index 0000000000000..06f8a0c288d7f --- /dev/null +++ b/src/tools/rustfmt/tests/writemode/source/stdin.rs @@ -0,0 +1,6 @@ + +fn + some( ) +{ +} +fn main () {} diff --git a/src/tools/rustfmt/tests/writemode/target/output.json b/src/tools/rustfmt/tests/writemode/target/output.json index b5f327b0a1ca6..d8b5467ee91ca 100644 --- a/src/tools/rustfmt/tests/writemode/target/output.json +++ b/src/tools/rustfmt/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}\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/src/tools/rustfmt/tests/writemode/target/stdin.json b/src/tools/rustfmt/tests/writemode/target/stdin.json new file mode 100644 index 0000000000000..dbf2c48632295 --- /dev/null +++ b/src/tools/rustfmt/tests/writemode/target/stdin.json @@ -0,0 +1 @@ +[{"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/src/tools/rustfmt/tests/writemode/target/stdin.xml b/src/tools/rustfmt/tests/writemode/target/stdin.xml new file mode 100644 index 0000000000000..a7301bbc553c3 --- /dev/null +++ b/src/tools/rustfmt/tests/writemode/target/stdin.xml @@ -0,0 +1,2 @@ + +