mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 08:44:35 +00:00
Merge commit '5ff7b632a95bac6955611d85040859128902c580' into sync-rustfmt-subtree
This commit is contained in:
commit
419df99e18
@ -40,6 +40,10 @@ jobs:
|
||||
rustc -Vv
|
||||
cargo -V
|
||||
cargo build
|
||||
env:
|
||||
RUSTFLAGS: '-D warnings'
|
||||
|
||||
- name: test
|
||||
run: cargo test
|
||||
env:
|
||||
RUSTFLAGS: '-D warnings'
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
]
|
||||
|
@ -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`
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2021-12-29"
|
||||
channel = "nightly-2022-03-27"
|
||||
components = ["rustc-dev"]
|
||||
|
@ -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);
|
||||
|
@ -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<IoError> for OperationError {
|
||||
@ -255,15 +251,20 @@ fn format_string(input: String, options: GetOptsOptions) -> Result<i32> {
|
||||
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] <file>...",
|
||||
sep,
|
||||
env::args_os().next().unwrap().to_string_lossy()
|
||||
"{}Format Rust code\n\nusage: rustfmt [options] <file>...",
|
||||
sep
|
||||
);
|
||||
println!("{}", opts.usage(&msg));
|
||||
}
|
||||
|
@ -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(),
|
||||
};
|
||||
|
||||
|
@ -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<String>,
|
||||
/// 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;
|
||||
}
|
||||
}
|
||||
|
@ -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, "<stdin>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Option<PathBuf>, 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
|
||||
|
@ -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<EmitterResult, io::Error> {
|
||||
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<T>(
|
||||
mut writer: T,
|
||||
filename: &Path,
|
||||
filename: &FileName,
|
||||
diff: Vec<Mismatch>,
|
||||
) -> Result<(), io::Error>
|
||||
where
|
||||
T: Write,
|
||||
{
|
||||
write!(writer, r#"<file name="{}">"#, filename.display())?;
|
||||
write!(writer, r#"<file name="{}">"#, 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="{}"></file>"#, file_name).as_bytes()
|
||||
|
@ -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 });
|
||||
}
|
||||
|
||||
|
@ -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<MismatchedFile>,
|
||||
}
|
||||
|
||||
#[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<MismatchedBlock>,
|
||||
}
|
||||
|
||||
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<EmitterResult, io::Error> {
|
||||
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<T>(
|
||||
mut writer: T,
|
||||
filename: &Path,
|
||||
diff: Vec<Mismatch>,
|
||||
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<Mismatch>,
|
||||
) -> 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)
|
||||
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::Resulting(msg) => {
|
||||
original_end_line = original_begin_line + original_line_counter;
|
||||
original_line_counter += 1;
|
||||
original_lines.push(msg)
|
||||
}
|
||||
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<T>() where T: Bar {"),
|
||||
expected: String::from("fn Foo<T>()\nwhere\n T: Bar,\n{"),
|
||||
original: String::from("fn Foo<T>() where T: Bar {\n"),
|
||||
expected: String::from("fn Foo<T>()\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());
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -190,13 +190,17 @@ pub(crate) fn merge_use_trees(use_trees: Vec<UseTree>, 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<UseTree>) -> Vec<UseTree> {
|
||||
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}")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -611,15 +611,30 @@ pub(crate) fn extract_post_comment(
|
||||
post_snippet: &str,
|
||||
comment_end: usize,
|
||||
separator: &str,
|
||||
is_last: bool,
|
||||
) -> Option<String> {
|
||||
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);
|
||||
|
||||
|
@ -112,6 +112,7 @@ fn rewrite_macro_name(
|
||||
fn return_macro_parse_failure_fallback(
|
||||
context: &RewriteContext<'_>,
|
||||
indent: Indent,
|
||||
position: MacroPosition,
|
||||
span: Span,
|
||||
) -> Option<String> {
|
||||
// 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<String> {
|
||||
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(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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<symbol::Ident>,
|
||||
dir_path: &Path,
|
||||
) -> Result<rustc_expand::module::ModulePathSuccess, rustc_expand::module::ModError<'_>> {
|
||||
rustc_expand::module::default_submod_path(&self.parse_sess, id, relative, dir_path)
|
||||
) -> Result<ModulePathSuccess, ModError<'_>> {
|
||||
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)
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -290,3 +290,33 @@ fn get_code_blocks() -> Vec<ConfigCodeBlock> {
|
||||
|
||||
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<number>\d+)\]\((?P<link>\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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<u8> = 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, "<stdin>:\n\nfn main() {}\n".as_bytes());
|
||||
#[cfg(windows)]
|
||||
assert_eq!(buf, "stdin:\n\nfn main() {}\r\n".as_bytes());
|
||||
assert_eq!(buf, "<stdin>:\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(),
|
||||
"<stdin>:\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(), "<stdin>\n");
|
||||
}
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
||||
}
|
||||
|
@ -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(' ');
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -160,8 +160,18 @@ pub(crate) fn rewrite_with_alignment<T: AlignedItem>(
|
||||
};
|
||||
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<T: AlignedItem>(
|
||||
span: Span,
|
||||
offset: Indent,
|
||||
one_line_width: usize,
|
||||
force_trailing_separator: bool,
|
||||
) -> Option<String> {
|
||||
// 1 = ","
|
||||
let item_shape = Shape::indented(offset, context.config).sub_width(1)?;
|
||||
@ -246,9 +257,15 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
|
||||
});
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -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())))
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
mod a;
|
1
src/tools/rustfmt/tests/mod-resolver/issue-5198/a.rs
Normal file
1
src/tools/rustfmt/tests/mod-resolver/issue-5198/a.rs
Normal file
@ -0,0 +1 @@
|
||||
fn main( ) { println!("Hello World!") }
|
3
src/tools/rustfmt/tests/mod-resolver/issue-5198/lib.rs
Normal file
3
src/tools/rustfmt/tests/mod-resolver/issue-5198/lib.rs
Normal file
@ -0,0 +1,3 @@
|
||||
mod a;
|
||||
mod b;
|
||||
mod c;
|
1
src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/b.rs
Normal file
1
src/tools/rustfmt/tests/mod-resolver/issue-5198/lib/b.rs
Normal file
@ -0,0 +1 @@
|
||||
fn main( ) { println!("Hello World!") }
|
@ -0,0 +1,3 @@
|
||||
mod e;
|
||||
mod f;
|
||||
mod g;
|
@ -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.
|
@ -0,0 +1 @@
|
||||
fn main( ) { println!("Hello World!") }
|
@ -0,0 +1 @@
|
||||
fn main( ) { println!("Hello World!") }
|
@ -0,0 +1 @@
|
||||
fn main( ) { println!("Hello World!") }
|
@ -0,0 +1,3 @@
|
||||
mod d;
|
||||
|
||||
fn main( ) { println!("Hello World!") }
|
@ -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.
|
@ -0,0 +1,3 @@
|
||||
// module resolution fails because the path does not exist.
|
||||
#[path = "path/to/does_not_exist.rs"]
|
||||
mod a;
|
@ -0,0 +1,2 @@
|
||||
// module resolution fails because `./a/b.rs` does not exist
|
||||
mod b;
|
@ -0,0 +1 @@
|
||||
mod a;
|
@ -0,0 +1,2 @@
|
||||
// module resolution fails because `./a.rs` does not exist
|
||||
mod a;
|
@ -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]
|
@ -0,0 +1,7 @@
|
||||
pub fn foo() -> i32 {
|
||||
3
|
||||
}
|
||||
|
||||
pub fn bar() -> i32 {
|
||||
4
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
mod test1 {
|
||||
#[cfg(unix)]
|
||||
mod sub1;
|
||||
#[cfg(not(unix))]
|
||||
mod sub2;
|
||||
|
||||
mod sub3;
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
use rustfmt_test_submodule_issue::foo;
|
||||
|
||||
#[test]
|
||||
fn test_foo() {
|
||||
assert_eq!(3, foo());
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
use rustfmt_test_submodule_issue::bar;
|
||||
|
||||
#[test]
|
||||
fn test_bar() {
|
||||
assert_eq!(4, bar());
|
||||
}
|
@ -0,0 +1 @@
|
||||
mod sub4;
|
@ -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] <file>..."));
|
||||
}
|
||||
|
||||
#[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"));
|
||||
}
|
||||
|
14
src/tools/rustfmt/tests/source/5131_crate.rs
Normal file
14
src/tools/rustfmt/tests/source/5131_crate.rs
Normal file
@ -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;
|
33
src/tools/rustfmt/tests/source/5131_module.rs
Normal file
33
src/tools/rustfmt/tests/source/5131_module.rs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
15
src/tools/rustfmt/tests/source/5131_one.rs
Normal file
15
src/tools/rustfmt/tests/source/5131_one.rs
Normal file
@ -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;
|
@ -0,0 +1,11 @@
|
||||
// rustfmt-short_array_element_width_threshold: 10
|
||||
|
||||
fn main() {
|
||||
pub const FORMAT_TEST: [u64; 5] = [
|
||||
0x0000000000000000,
|
||||
0xaaaaaaaaaaaaaaaa,
|
||||
0xbbbbbbbbbbbbbbbb,
|
||||
0xcccccccccccccccc,
|
||||
0xdddddddddddddddd,
|
||||
];
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
// rustfmt-short_array_element_width_threshold: 20
|
||||
|
||||
fn main() {
|
||||
pub const FORMAT_TEST: [u64; 5] = [
|
||||
0x0000000000000000,
|
||||
0xaaaaaaaaaaaaaaaa,
|
||||
0xbbbbbbbbbbbbbbbb,
|
||||
0xcccccccccccccccc,
|
||||
0xdddddddddddddddd,
|
||||
];
|
||||
}
|
@ -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,
|
||||
];
|
||||
}
|
@ -77,3 +77,16 @@ libc::c_long;
|
||||
extern {
|
||||
|
||||
}
|
||||
|
||||
macro_rules! x {
|
||||
($tt:tt) => {};
|
||||
}
|
||||
|
||||
extern "macros" {
|
||||
x!(ident);
|
||||
x!(#);
|
||||
x![ident];
|
||||
x![#];
|
||||
x! {ident}
|
||||
x! {#}
|
||||
}
|
||||
|
@ -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};
|
||||
|
11
src/tools/rustfmt/tests/source/issue-4036/one.rs
Normal file
11
src/tools/rustfmt/tests/source/issue-4036/one.rs
Normal file
@ -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.",
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
12
src/tools/rustfmt/tests/source/issue-4036/three.rs
Normal file
12
src/tools/rustfmt/tests/source/issue-4036/three.rs
Normal file
@ -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.",
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
11
src/tools/rustfmt/tests/source/issue-4036/two.rs
Normal file
11
src/tools/rustfmt/tests/source/issue-4036/two.rs
Normal file
@ -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.",
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
14
src/tools/rustfmt/tests/source/issue-4791/buggy.rs
Normal file
14
src/tools/rustfmt/tests/source/issue-4791/buggy.rs
Normal file
@ -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
|
||||
}
|
14
src/tools/rustfmt/tests/source/issue-4791/trailing_comma.rs
Normal file
14
src/tools/rustfmt/tests/source/issue-4791/trailing_comma.rs
Normal file
@ -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,
|
||||
}
|
22
src/tools/rustfmt/tests/source/issue-5023.rs
Normal file
22
src/tools/rustfmt/tests/source/issue-5023.rs
Normal file
@ -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();
|
||||
}
|
||||
}
|
@ -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
|
||||
// ...,
|
||||
/* ...
|
||||
*/,
|
||||
);
|
||||
}
|
@ -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
|
||||
// ...
|
||||
/* ...
|
||||
*/
|
||||
);
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
fn main() {
|
||||
let _ = std::ops::Add::add(10, 20
|
||||
// ...,
|
||||
);
|
||||
|
||||
let _ = std::ops::Add::add(10, 20
|
||||
/* ... */,
|
||||
);
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
fn main() {
|
||||
let _ = std::ops::Add::add(10, 20
|
||||
// ...
|
||||
);
|
||||
|
||||
let _ = std::ops::Add::add(10, 20
|
||||
/* ... */
|
||||
);
|
||||
}
|
||||
|
@ -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() {}
|
@ -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() {}
|
@ -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() {}
|
@ -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`
|
||||
}
|
@ -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`
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
113
src/tools/rustfmt/tests/source/issue_4854.rs
Normal file
113
src/tools/rustfmt/tests/source/issue_4854.rs
Normal file
@ -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 */ }
|
||||
}
|
9
src/tools/rustfmt/tests/target/5131_crate.rs
Normal file
9
src/tools/rustfmt/tests/target/5131_crate.rs
Normal file
@ -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};
|
32
src/tools/rustfmt/tests/target/5131_module.rs
Normal file
32
src/tools/rustfmt/tests/target/5131_module.rs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
12
src/tools/rustfmt/tests/target/5131_one.rs
Normal file
12
src/tools/rustfmt/tests/target/5131_one.rs
Normal file
@ -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},
|
||||
};
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -0,0 +1,11 @@
|
||||
// rustfmt-short_array_element_width_threshold: 10
|
||||
|
||||
fn main() {
|
||||
pub const FORMAT_TEST: [u64; 5] = [
|
||||
0x0000000000000000,
|
||||
0xaaaaaaaaaaaaaaaa,
|
||||
0xbbbbbbbbbbbbbbbb,
|
||||
0xcccccccccccccccc,
|
||||
0xdddddddddddddddd,
|
||||
];
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
// rustfmt-short_array_element_width_threshold: 20
|
||||
|
||||
fn main() {
|
||||
pub const FORMAT_TEST: [u64; 5] = [
|
||||
0x0000000000000000, 0xaaaaaaaaaaaaaaaa, 0xbbbbbbbbbbbbbbbb, 0xcccccccccccccccc,
|
||||
0xdddddddddddddddd,
|
||||
];
|
||||
}
|
@ -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,
|
||||
];
|
||||
}
|
14
src/tools/rustfmt/tests/target/doc-of-generic-item.rs
Normal file
14
src/tools/rustfmt/tests/target/doc-of-generic-item.rs
Normal file
@ -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,
|
||||
>;
|
@ -82,3 +82,16 @@ extern "C" {
|
||||
}
|
||||
|
||||
extern "C" {}
|
||||
|
||||
macro_rules! x {
|
||||
($tt:tt) => {};
|
||||
}
|
||||
|
||||
extern "macros" {
|
||||
x!(ident);
|
||||
x!(#);
|
||||
x![ident];
|
||||
x![#];
|
||||
x! {ident}
|
||||
x! {#}
|
||||
}
|
||||
|
@ -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)]
|
||||
|
12
src/tools/rustfmt/tests/target/issue-4036/one.rs
Normal file
12
src/tools/rustfmt/tests/target/issue-4036/one.rs
Normal file
@ -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.",
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
17
src/tools/rustfmt/tests/target/issue-4036/three.rs
Normal file
17
src/tools/rustfmt/tests/target/issue-4036/three.rs
Normal file
@ -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.",
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
16
src/tools/rustfmt/tests/target/issue-4036/two.rs
Normal file
16
src/tools/rustfmt/tests/target/issue-4036/two.rs
Normal file
@ -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.",
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
14
src/tools/rustfmt/tests/target/issue-4791/buggy.rs
Normal file
14
src/tools/rustfmt/tests/target/issue-4791/buggy.rs
Normal file
@ -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
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user